Skip to content

Commit ecdc197

Browse files
committed
Add default descriptions for Hibernate Validator's constraints
Closes gh-151
1 parent e5aa5ba commit ecdc197

File tree

3 files changed

+233
-59
lines changed

3 files changed

+233
-59
lines changed

docs/src/docs/asciidoc/documenting-your-api.adoc

+15
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,21 @@ Default descriptions are provided for all of the Bean Validation 1.1's constrain
534534
* Pattern
535535
* Size
536536

537+
Default descriptions are also provided for constraints from Hibernate Validator:
538+
539+
* CreditCardNumber
540+
* EAN
541+
* Email
542+
* Length
543+
* LuhnCheck
544+
* Mod10Check
545+
* Mod11Check
546+
* NotBlank
547+
* NotEmpty
548+
* Range
549+
* SafeHtml
550+
* URL
551+
537552
To override the default descriptions, or to provide a new description, create a resource
538553
bundle with the base name
539554
`org.springframework.restdocs.constraints.ConstraintDescriptions`. The Spring

spring-restdocs-core/src/main/resources/org/springframework/restdocs/constraints/DefaultConstraintDescriptions.properties

+13-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,16 @@ javax.validation.constraints.NotNull.description=Must not be null
1010
javax.validation.constraints.Null.description=Must be null
1111
javax.validation.constraints.Past.description=Must be in the past
1212
javax.validation.constraints.Pattern.description=Must match the regular expression '${regexp}'
13-
javax.validation.constraints.Size.description=Size must be between ${min} and ${max} inclusive
13+
javax.validation.constraints.Size.description=Size must be between ${min} and ${max} inclusive
14+
org.hibernate.validator.constraints.CreditCardNumber.description=Must be a well-formed credit card number
15+
org.hibernate.validator.constraints.EAN.description=Must be a well-formed ${type} number
16+
org.hibernate.validator.constraints.Email.description=Must be a well-formed email address
17+
org.hibernate.validator.constraints.Length.description=Length must be between ${min} and ${max} inclusive
18+
org.hibernate.validator.constraints.LuhnCheck.description=Must pass the Luhn Modulo 10 checksum algorithm
19+
org.hibernate.validator.constraints.Mod10Check.description=Must pass the Mod10 checksum algorithm
20+
org.hibernate.validator.constraints.Mod11Check.description=Must pass the Mod11 checksum algorithm
21+
org.hibernate.validator.constraints.NotBlank.description=Must not be blank
22+
org.hibernate.validator.constraints.NotEmpty.description=Must not be empty
23+
org.hibernate.validator.constraints.Range.description=Must be at least ${min} and at most ${max}
24+
org.hibernate.validator.constraints.SafeHtml.description=Must be safe HTML
25+
org.hibernate.validator.constraints.URL.description=Must be a well-formed URL

spring-restdocs-core/src/test/java/org/springframework/restdocs/constraints/ResourceBundleConstraintDescriptionResolverTests.java

+205-58
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
package org.springframework.restdocs.constraints;
1818

19+
import java.lang.annotation.Annotation;
20+
import java.math.BigDecimal;
1921
import java.net.URL;
2022
import java.util.Collections;
21-
import java.util.HashMap;
23+
import java.util.Date;
24+
import java.util.List;
2225
import java.util.ListResourceBundle;
23-
import java.util.Map;
2426
import java.util.ResourceBundle;
2527

2628
import javax.validation.constraints.AssertFalse;
@@ -37,8 +39,23 @@
3739
import javax.validation.constraints.Pattern;
3840
import javax.validation.constraints.Size;
3941

42+
import org.hibernate.validator.constraints.CreditCardNumber;
43+
import org.hibernate.validator.constraints.EAN;
44+
import org.hibernate.validator.constraints.Email;
45+
import org.hibernate.validator.constraints.Length;
46+
import org.hibernate.validator.constraints.LuhnCheck;
47+
import org.hibernate.validator.constraints.Mod10Check;
48+
import org.hibernate.validator.constraints.Mod11Check;
49+
import org.hibernate.validator.constraints.NotBlank;
50+
import org.hibernate.validator.constraints.NotEmpty;
51+
import org.hibernate.validator.constraints.Range;
52+
import org.hibernate.validator.constraints.SafeHtml;
4053
import org.junit.Test;
4154

55+
import org.springframework.core.annotation.AnnotationUtils;
56+
import org.springframework.util.Assert;
57+
import org.springframework.util.ReflectionUtils;
58+
4259
import static org.hamcrest.CoreMatchers.equalTo;
4360
import static org.hamcrest.CoreMatchers.is;
4461
import static org.junit.Assert.assertThat;
@@ -54,111 +71,151 @@ public class ResourceBundleConstraintDescriptionResolverTests {
5471

5572
@Test
5673
public void defaultMessageAssertFalse() {
57-
String description = this.resolver.resolveDescription(new Constraint(
58-
AssertFalse.class.getName(), Collections.<String, Object>emptyMap()));
59-
assertThat(description, is(equalTo("Must be false")));
74+
assertThat(constraintDescriptionForField("assertFalse"),
75+
is(equalTo("Must be false")));
6076
}
6177

6278
@Test
6379
public void defaultMessageAssertTrue() {
64-
String description = this.resolver.resolveDescription(new Constraint(
65-
AssertTrue.class.getName(), Collections.<String, Object>emptyMap()));
66-
assertThat(description, is(equalTo("Must be true")));
80+
assertThat(constraintDescriptionForField("assertTrue"),
81+
is(equalTo("Must be true")));
6782
}
6883

6984
@Test
7085
public void defaultMessageDecimalMax() {
71-
Map<String, Object> configuration = new HashMap<>();
72-
configuration.put("value", "9.875");
73-
String description = this.resolver.resolveDescription(
74-
new Constraint(DecimalMax.class.getName(), configuration));
75-
assertThat(description, is(equalTo("Must be at most 9.875")));
86+
assertThat(constraintDescriptionForField("decimalMax"),
87+
is(equalTo("Must be at most 9.875")));
7688
}
7789

7890
@Test
7991
public void defaultMessageDecimalMin() {
80-
Map<String, Object> configuration = new HashMap<>();
81-
configuration.put("value", "1.5");
82-
String description = this.resolver.resolveDescription(
83-
new Constraint(DecimalMin.class.getName(), configuration));
84-
assertThat(description, is(equalTo("Must be at least 1.5")));
92+
assertThat(constraintDescriptionForField("decimalMin"),
93+
is(equalTo("Must be at least 1.5")));
8594
}
8695

8796
@Test
8897
public void defaultMessageDigits() {
89-
Map<String, Object> configuration = new HashMap<>();
90-
configuration.put("integer", "2");
91-
configuration.put("fraction", "5");
92-
String description = this.resolver.resolveDescription(
93-
new Constraint(Digits.class.getName(), configuration));
94-
assertThat(description, is(equalTo(
95-
"Must have at most 2 integral digits and 5 " + "fractional digits")));
98+
assertThat(constraintDescriptionForField("digits"), is(
99+
equalTo("Must have at most 2 integral digits and 5 fractional digits")));
96100
}
97101

98102
@Test
99103
public void defaultMessageFuture() {
100-
String description = this.resolver.resolveDescription(new Constraint(
101-
Future.class.getName(), Collections.<String, Object>emptyMap()));
102-
assertThat(description, is(equalTo("Must be in the future")));
104+
assertThat(constraintDescriptionForField("future"),
105+
is(equalTo("Must be in the future")));
103106
}
104107

105108
@Test
106109
public void defaultMessageMax() {
107-
Map<String, Object> configuration = new HashMap<>();
108-
configuration.put("value", 10);
109-
String description = this.resolver
110-
.resolveDescription(new Constraint(Max.class.getName(), configuration));
111-
assertThat(description, is(equalTo("Must be at most 10")));
110+
assertThat(constraintDescriptionForField("max"),
111+
is(equalTo("Must be at most 10")));
112112
}
113113

114114
@Test
115115
public void defaultMessageMin() {
116-
Map<String, Object> configuration = new HashMap<>();
117-
configuration.put("value", 10);
118-
String description = this.resolver
119-
.resolveDescription(new Constraint(Min.class.getName(), configuration));
120-
assertThat(description, is(equalTo("Must be at least 10")));
116+
assertThat(constraintDescriptionForField("min"),
117+
is(equalTo("Must be at least 10")));
121118
}
122119

123120
@Test
124121
public void defaultMessageNotNull() {
125-
String description = this.resolver.resolveDescription(new Constraint(
126-
NotNull.class.getName(), Collections.<String, Object>emptyMap()));
127-
assertThat(description, is(equalTo("Must not be null")));
122+
assertThat(constraintDescriptionForField("notNull"),
123+
is(equalTo("Must not be null")));
128124
}
129125

130126
@Test
131127
public void defaultMessageNull() {
132-
String description = this.resolver.resolveDescription(new Constraint(
133-
Null.class.getName(), Collections.<String, Object>emptyMap()));
134-
assertThat(description, is(equalTo("Must be null")));
128+
assertThat(constraintDescriptionForField("nul"), is(equalTo("Must be null")));
135129
}
136130

137131
@Test
138132
public void defaultMessagePast() {
139-
String description = this.resolver.resolveDescription(new Constraint(
140-
Past.class.getName(), Collections.<String, Object>emptyMap()));
141-
assertThat(description, is(equalTo("Must be in the past")));
133+
assertThat(constraintDescriptionForField("past"),
134+
is(equalTo("Must be in the past")));
142135
}
143136

144137
@Test
145138
public void defaultMessagePattern() {
146-
Map<String, Object> configuration = new HashMap<>();
147-
configuration.put("regexp", "[A-Z][a-z]+");
148-
String description = this.resolver.resolveDescription(
149-
new Constraint(Pattern.class.getName(), configuration));
150-
assertThat(description,
139+
assertThat(constraintDescriptionForField("pattern"),
151140
is(equalTo("Must match the regular expression '[A-Z][a-z]+'")));
152141
}
153142

154143
@Test
155144
public void defaultMessageSize() {
156-
Map<String, Object> configuration = new HashMap<>();
157-
configuration.put("min", 2);
158-
configuration.put("max", 10);
159-
String description = this.resolver
160-
.resolveDescription(new Constraint(Size.class.getName(), configuration));
161-
assertThat(description, is(equalTo("Size must be between 2 and 10 inclusive")));
145+
assertThat(constraintDescriptionForField("size"),
146+
is(equalTo("Size must be between 2 and 10 inclusive")));
147+
}
148+
149+
@Test
150+
public void defaultMessageCreditCardNumber() {
151+
assertThat(constraintDescriptionForField("creditCardNumber"),
152+
is(equalTo("Must be a well-formed credit card number")));
153+
}
154+
155+
@Test
156+
public void defaultMessageEan() {
157+
assertThat(constraintDescriptionForField("ean"),
158+
is(equalTo("Must be a well-formed EAN13 number")));
159+
}
160+
161+
@Test
162+
public void defaultMessageEmail() {
163+
assertThat(constraintDescriptionForField("email"),
164+
is(equalTo("Must be a well-formed email address")));
165+
}
166+
167+
@Test
168+
public void defaultMessageLength() {
169+
assertThat(constraintDescriptionForField("length"),
170+
is(equalTo("Length must be between 2 and 10 inclusive")));
171+
}
172+
173+
@Test
174+
public void defaultMessageLuhnCheck() {
175+
assertThat(constraintDescriptionForField("luhnCheck"),
176+
is(equalTo("Must pass the Luhn Modulo 10 checksum algorithm")));
177+
}
178+
179+
@Test
180+
public void defaultMessageMod10Check() {
181+
assertThat(constraintDescriptionForField("mod10Check"),
182+
is(equalTo("Must pass the Mod10 checksum algorithm")));
183+
}
184+
185+
@Test
186+
public void defaultMessageMod11Check() {
187+
assertThat(constraintDescriptionForField("mod11Check"),
188+
is(equalTo("Must pass the Mod11 checksum algorithm")));
189+
}
190+
191+
@Test
192+
public void defaultMessageNotBlank() {
193+
assertThat(constraintDescriptionForField("notBlank"),
194+
is(equalTo("Must not be blank")));
195+
}
196+
197+
@Test
198+
public void defaultMessageNotEmpty() {
199+
assertThat(constraintDescriptionForField("notEmpty"),
200+
is(equalTo("Must not be empty")));
201+
}
202+
203+
@Test
204+
public void defaultMessageRange() {
205+
assertThat(constraintDescriptionForField("range"),
206+
is(equalTo("Must be at least 10 and at most 100")));
207+
}
208+
209+
@Test
210+
public void defaultMessageSafeHtml() {
211+
assertThat(constraintDescriptionForField("safeHtml"),
212+
is(equalTo("Must be safe HTML")));
213+
}
214+
215+
@Test
216+
public void defaultMessageUrl() {
217+
assertThat(constraintDescriptionForField("url"),
218+
is(equalTo("Must be a well-formed URL")));
162219
}
163220

164221
@Test
@@ -206,4 +263,94 @@ protected Object[][] getContents() {
206263
assertThat(description, is(equalTo("Not null")));
207264
}
208265

266+
private String constraintDescriptionForField(String name) {
267+
return this.resolver.resolveDescription(getConstraintFromField(name));
268+
}
269+
270+
private Constraint getConstraintFromField(String name) {
271+
Annotation[] annotations = ReflectionUtils.findField(Constrained.class, name)
272+
.getAnnotations();
273+
Assert.isTrue(annotations.length == 1);
274+
return new Constraint(annotations[0].annotationType().getName(),
275+
AnnotationUtils.getAnnotationAttributes(annotations[0]));
276+
}
277+
278+
private static class Constrained {
279+
280+
@AssertFalse
281+
private boolean assertFalse;
282+
283+
@AssertTrue
284+
private boolean assertTrue;
285+
286+
@DecimalMax("9.875")
287+
private BigDecimal decimalMax;
288+
289+
@DecimalMin("1.5")
290+
private BigDecimal decimalMin;
291+
292+
@Digits(integer = 2, fraction = 5)
293+
private String digits;
294+
295+
@Future
296+
private Date future;
297+
298+
@Max(10)
299+
private int max;
300+
301+
@Min(10)
302+
private int min;
303+
304+
@NotNull
305+
private String notNull;
306+
307+
@Null
308+
private String nul;
309+
310+
@Past
311+
private Date past;
312+
313+
@Pattern(regexp = "[A-Z][a-z]+")
314+
private String pattern;
315+
316+
@Size(min = 2, max = 10)
317+
private List<String> size;
318+
319+
@CreditCardNumber
320+
private String creditCardNumber;
321+
322+
@EAN
323+
private String ean;
324+
325+
@Email
326+
private String email;
327+
328+
@Length(min = 2, max = 10)
329+
private String length;
330+
331+
@LuhnCheck
332+
private String luhnCheck;
333+
334+
@Mod10Check
335+
private String mod10Check;
336+
337+
@Mod11Check
338+
private String mod11Check;
339+
340+
@NotBlank
341+
private String notBlank;
342+
343+
@NotEmpty
344+
private String notEmpty;
345+
346+
@Range(min = 10, max = 100)
347+
private int range;
348+
349+
@SafeHtml
350+
private String safeHtml;
351+
352+
@org.hibernate.validator.constraints.URL
353+
private String url;
354+
}
355+
209356
}

0 commit comments

Comments
 (0)