diff --git a/build.gradle b/build.gradle index 42e043b3..b58d8013 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,8 @@ nohttp { ext { javadocLinks = [ "https://docs.spring.io/spring-framework/docs/$springFrameworkVersion/javadoc-api/", - "https://docs.jboss.org/hibernate/validator/7.0/api/", - "https://jakarta.ee/specifications/bean-validation/3.0/apidocs/" + "https://docs.jboss.org/hibernate/validator/9.0/api/", + "https://jakarta.ee/specifications/bean-validation/3.1/apidocs/" ] as String[] } diff --git a/docs/src/docs/asciidoc/documenting-your-api.adoc b/docs/src/docs/asciidoc/documenting-your-api.adoc index 55fa120e..cb9ce82a 100644 --- a/docs/src/docs/asciidoc/documenting-your-api.adoc +++ b/docs/src/docs/asciidoc/documenting-your-api.adoc @@ -1146,7 +1146,7 @@ To take complete control of constraint resolution, you can use your own implemen [[documenting-your-api-constraints-describing]] ==== Describing Constraints -Default descriptions are provided for all of Bean Validation 3.0's constraints: +Default descriptions are provided for all of Bean Validation 3.1's constraints: * `AssertFalse` * `AssertTrue` diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java index 1c409a6f..59788d06 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2022 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,7 +64,7 @@ import org.springframework.util.StringUtils; * {@code jakarta.validation.constraints.NotNull} is * {@code jakarta.validation.constraints.NotNull.description}. *

- * Default descriptions are provided for Bean Validation 2.0's constraints: + * Default descriptions are provided for all of Bean Validation 3.1's constraints: * *

* *

- * Default descriptions are also provided for Hibernate Validator's constraints: + * Default descriptions are also provided for the following Hibernate Validator + * constraints: * *

diff --git a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties index 248cb238..699b900e 100644 --- a/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties +++ b/spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties @@ -24,12 +24,9 @@ org.hibernate.validator.constraints.CodePointLength.description=Code point lengt org.hibernate.validator.constraints.CreditCardNumber.description=Must be a well-formed credit card number org.hibernate.validator.constraints.Currency.description=Must be in an accepted currency unit (${value}) org.hibernate.validator.constraints.EAN.description=Must be a well-formed ${type} number -org.hibernate.validator.constraints.Email.description=Must be a well-formed email address org.hibernate.validator.constraints.Length.description=Length must be between ${min} and ${max} inclusive org.hibernate.validator.constraints.LuhnCheck.description=Must pass the Luhn Modulo 10 checksum algorithm org.hibernate.validator.constraints.Mod10Check.description=Must pass the Mod10 checksum algorithm org.hibernate.validator.constraints.Mod11Check.description=Must pass the Mod11 checksum algorithm -org.hibernate.validator.constraints.NotBlank.description=Must not be blank -org.hibernate.validator.constraints.NotEmpty.description=Must not be empty org.hibernate.validator.constraints.Range.description=Must be at least ${min} and at most ${max} org.hibernate.validator.constraints.URL.description=Must be a well-formed URL \ No newline at end of file diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java index 4598cd1a..f4b5e2af 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java +++ b/spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2024 the original author or authors. + * Copyright 2014-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,11 @@ import java.math.BigDecimal; import java.net.URL; import java.util.Collections; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.ListResourceBundle; import java.util.ResourceBundle; +import java.util.Set; import javax.money.MonetaryAmount; @@ -61,7 +63,11 @@ import org.hibernate.validator.constraints.Range; import org.junit.Test; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -180,12 +186,6 @@ public class ResourceBundleConstraintDescriptionResolverTests { assertThat(constraintDescriptionForField("email")).isEqualTo("Must be a well-formed email address"); } - @Test - public void defaultMessageEmailHibernateValidator() { - assertThat(constraintDescriptionForField("emailHibernateValidator")) - .isEqualTo("Must be a well-formed email address"); - } - @Test public void defaultMessageLength() { assertThat(constraintDescriptionForField("length")).isEqualTo("Length must be between 2 and 10 inclusive"); @@ -222,11 +222,6 @@ public class ResourceBundleConstraintDescriptionResolverTests { assertThat(constraintDescriptionForField("notBlank")).isEqualTo("Must not be blank"); } - @Test - public void defaultMessageNotBlankHibernateValidator() { - assertThat(constraintDescriptionForField("notBlankHibernateValidator")).isEqualTo("Must not be blank"); - } - @Test public void defaultMessageNotEmpty() { assertThat(constraintDescriptionForField("notEmpty")).isEqualTo("Must not be empty"); @@ -298,6 +293,29 @@ public class ResourceBundleConstraintDescriptionResolverTests { assertThat(description).isEqualTo("Not null"); } + @Test + public void allBeanValidationConstraintsAreTested() throws Exception { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("jakarta/validation/constraints/*.class"); + Set> beanValidationConstraints = new HashSet<>(); + for (Resource resource : resources) { + String className = ClassUtils.convertResourcePathToClassName(((ClassPathResource) resource).getPath()); + if (className.endsWith(".class")) { + className = className.substring(0, className.length() - 6); + } + Class type = Class.forName(className); + if (type.isAnnotation() && type.isAnnotationPresent(jakarta.validation.Constraint.class)) { + beanValidationConstraints.add(type); + } + } + ReflectionUtils.doWithFields(Constrained.class, (field) -> { + for (Annotation annotation : field.getAnnotations()) { + beanValidationConstraints.remove(annotation.annotationType()); + } + }); + assertThat(beanValidationConstraints).isEmpty(); + } + private String constraintDescriptionForField(String name) { return this.resolver.resolveDescription(getConstraintFromField(name)); } @@ -372,10 +390,6 @@ public class ResourceBundleConstraintDescriptionResolverTests { @Email private String email; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.Email - private String emailHibernateValidator; - @Length(min = 2, max = 10) private String length; @@ -397,17 +411,9 @@ public class ResourceBundleConstraintDescriptionResolverTests { @NotBlank private String notBlank; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.NotBlank - private String notBlankHibernateValidator; - @NotEmpty private String notEmpty; - @SuppressWarnings("deprecation") - @org.hibernate.validator.constraints.NotEmpty - private String notEmptyHibernateValidator; - @Positive private int positive; diff --git a/spring-restdocs-platform/build.gradle b/spring-restdocs-platform/build.gradle index 973cf732..4b2637cf 100644 --- a/spring-restdocs-platform/build.gradle +++ b/spring-restdocs-platform/build.gradle @@ -10,7 +10,7 @@ dependencies { constraints { api("com.samskivert:jmustache:$jmustacheVersion") api("jakarta.servlet:jakarta.servlet-api:6.0.0") - api("jakarta.validation:jakarta.validation-api:3.0.0") + api("jakarta.validation:jakarta.validation-api:3.1.0") api("junit:junit:4.13.1") api("org.apache.pdfbox:pdfbox:2.0.27") api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1") @@ -20,7 +20,7 @@ dependencies { api("org.assertj:assertj-core:3.23.1") api("org.hamcrest:hamcrest-core:1.3") api("org.hamcrest:hamcrest-library:1.3") - api("org.hibernate.validator:hibernate-validator:8.0.0.Final") + api("org.hibernate.validator:hibernate-validator:9.0.0.CR1") api("org.javamoney:moneta:1.4.2") api("org.junit.jupiter:junit-jupiter-api:5.0.0") }