spring-restdocs icon indicating copy to clipboard operation
spring-restdocs copied to clipboard

Add support for Bean Validation's constraint groups

Open AugustoRavazoli opened this issue 2 years ago • 2 comments

The docs gives an example of documenting constraints:

public void example() {
	ConstraintDescriptions userConstraints = new ConstraintDescriptions(UserInput.class);
	List<String> descriptions = userConstraints.descriptionsForProperty("name");
}

static class UserInput {

	@NotNull
	@Size(min = 1)
	String name;

	@NotNull
	@Size(min = 8)
	String password;

}

This way, you get all constraints descriptions for one property, but when using validation groups, how to get only the constraints descriptions for the given group? For example:

public void example() {
	ConstraintDescriptions userConstraints = new ConstraintDescriptions(UserInput.class);
        List<String> descriptions = userConstraints.descriptionsForProperty("email").groups(OnCreate.class);
}

static class UserInput {

	@NotNull
	String name;

        @NotNull(groups = OnCreate.class)
        @Null(groups = OnEdit.class)
        String email;
}

AugustoRavazoli avatar Apr 15 '23 18:04 AugustoRavazoli

There's no support for constraint groups at the moment.

wilkinsona avatar May 09 '23 16:05 wilkinsona

After reading the source code of the library and some tests, I found a possible solution using a custom ResourceBundleConstraintDescriptionResolver:

public class ConstrainedFields {

    private final ConstraintDescriptions constraints;
    private final List<Class<?>> groups;  // Constraints groups to exclude

    public ConstrainedFields(Class<?> clazz) {
      groups = new ArrayList<>();
      constraints = new ConstraintDescriptions(clazz, resolver(groups));
    }

    public FieldDescriptor pathExcludingGroups(String path, Class<?>... excludedGroups) {
      groups.addAll(List.of(excludedGroups));
      return path(path);
    }

    public FieldDescriptor path(String path) {
      var description = constraints.descriptionsForProperty(path).stream()
        .filter(s -> !s.isEmpty())
        .collect(joining(". "));
      return fieldWithPath(path).attributes(key("constraints").value(description));
    }

    private static ResourceBundleConstraintDescriptionResolver resolver(List<Class<?>> groups) {
      return new ResourceBundleConstraintDescriptionResolver() {

        @Override
        public String resolveDescription(Constraint constraint) {
          var description = super.resolveDescription(constraint);
          if (!groups.isEmpty()) {
            var constraintGroups = (Class<?>[]) constraint.getConfiguration().get("groups");
            if (List.of(constraintGroups).containsAll(groups)) {
              description = "";
              groups.clear();
            }
          }
          return description;
        }

      };
    }

  }

AugustoRavazoli avatar May 16 '23 19:05 AugustoRavazoli