diff --git a/blackbox-test/src/test/java/example/avaje/ACustomerMessageTest.java b/blackbox-test/src/test/java/example/avaje/ACustomerMessageTest.java index 1b07892c..234893b1 100644 --- a/blackbox-test/src/test/java/example/avaje/ACustomerMessageTest.java +++ b/blackbox-test/src/test/java/example/avaje/ACustomerMessageTest.java @@ -36,25 +36,25 @@ void blankDE() { @Test void sizeMax() { var violation = one(new ACustomer("NameIsTooLarge", "Other")); - assertThat(violation.message()).isEqualTo("size must be between 0 and 5"); + assertThat(violation.message()).isEqualTo("maximum length 5 exceeded"); } @Test void sizeMaxDE() { var violation = one(new ACustomer("NameIsTooLarge", "Other"), Locale.GERMAN); - assertThat(violation.message()).isEqualTo("Größe muss zwischen 0 und 5 sein"); + assertThat(violation.message()).isEqualTo("Länge muss zwischen 0 und 5 sein"); } @Test void sizeMinMax() { var violation = one(new ACustomer("valid", "Other", "TooLarge")); - assertThat(violation.message()).isEqualTo("size must be between 2 and 4"); + assertThat(violation.message()).isEqualTo("length must be between 2 and 4"); } @Test void sizeMinMaxDE() { var violation = one(new ACustomer("valid", "Other", "TooLarge"), Locale.GERMAN); - assertThat(violation.message()).isEqualTo("Größe muss zwischen 2 und 4 sein"); + assertThat(violation.message()).isEqualTo("Länge muss zwischen 2 und 4 sein"); } @Test diff --git a/blackbox-test/src/test/java/example/avaje/length/ALengthTest.java b/blackbox-test/src/test/java/example/avaje/length/ALengthTest.java index 18d360b1..698abda1 100644 --- a/blackbox-test/src/test/java/example/avaje/length/ALengthTest.java +++ b/blackbox-test/src/test/java/example/avaje/length/ALengthTest.java @@ -40,6 +40,19 @@ void lengthMax() { assertThat(violation.message()).isEqualTo("length must be between 1 and 3"); } + @Test + void lengthOnlyMax() { + var violation = one(new ALength("ok", "TooLargeHere", "ok", "ok")); + assertThat(violation.message()).isEqualTo("maximum length 5 exceeded"); + } + + @Test + void lengthOnlyMax_DE() { + // no {avaje.Length.max.message} for DE so uses fallback + var violation = one(new ALength("ok", "TooLargeHere", "ok", "ok"), Locale.GERMAN); + assertThat(violation.message()).isEqualTo("Länge muss zwischen 0 und 5 sein"); + } + @Test void lengthMaxDE() { var violation = one(new ALength("TooLarge", "ok", "ok", "ok"), Locale.GERMAN); @@ -49,7 +62,7 @@ void lengthMaxDE() { @Test void lengthMinMax() { var violation = one(new ACustomer("valid", "Other", "TooLarge")); - assertThat(violation.message()).isEqualTo("size must be between 2 and 4"); + assertThat(violation.message()).isEqualTo("length must be between 2 and 4"); } @Test diff --git a/blackbox-test/src/test/java/example/avaje/nested/ANestedTest.java b/blackbox-test/src/test/java/example/avaje/nested/ANestedTest.java index 38396071..3d51f85c 100644 --- a/blackbox-test/src/test/java/example/avaje/nested/ANestedTest.java +++ b/blackbox-test/src/test/java/example/avaje/nested/ANestedTest.java @@ -26,7 +26,7 @@ void validateNested() { var v0 = violations.get(0); assertThat(v0.path()).isEqualTo("lastName"); assertThat(v0.field()).isEqualTo("lastName"); - assertThat(v0.message()).isEqualTo("size must be between 0 and 5"); + assertThat(v0.message()).isEqualTo("maximum length 5 exceeded"); var v1 = violations.get(1); assertThat(v1.path()).isEqualTo("address.line1"); @@ -36,7 +36,7 @@ void validateNested() { var v2 = violations.get(2); assertThat(v2.path()).isEqualTo("address.line2"); assertThat(v2.field()).isEqualTo("line2"); - assertThat(v2.message()).isEqualTo("size must be between 0 and 4"); + assertThat(v2.message()).isEqualTo("maximum length 4 exceeded"); var v3 = violations.get(3); assertThat(v3.path()).isEqualTo("address.longValue"); diff --git a/blackbox-test/src/test/java/example/avaje/nested/ANestedWithNullableTest.java b/blackbox-test/src/test/java/example/avaje/nested/ANestedWithNullableTest.java index 44ed62ec..0beaf6c1 100644 --- a/blackbox-test/src/test/java/example/avaje/nested/ANestedWithNullableTest.java +++ b/blackbox-test/src/test/java/example/avaje/nested/ANestedWithNullableTest.java @@ -26,7 +26,7 @@ void validateNestedSkipNullAddress() { var v0 = violations.get(0); assertThat(v0.path()).isEqualTo("lastName"); assertThat(v0.field()).isEqualTo("lastName"); - assertThat(v0.message()).isEqualTo("size must be between 0 and 5"); + assertThat(v0.message()).isEqualTo("maximum length 5 exceeded"); } } @@ -44,7 +44,7 @@ void validateNestedAddress() { var v0 = violations.get(0); assertThat(v0.path()).isEqualTo("lastName"); assertThat(v0.field()).isEqualTo("lastName"); - assertThat(v0.message()).isEqualTo("size must be between 0 and 5"); + assertThat(v0.message()).isEqualTo("maximum length 5 exceeded"); } } @@ -62,7 +62,7 @@ void validateNested() { var v0 = violations.get(0); assertThat(v0.path()).isEqualTo("lastName"); assertThat(v0.field()).isEqualTo("lastName"); - assertThat(v0.message()).isEqualTo("size must be between 0 and 5"); + assertThat(v0.message()).isEqualTo("maximum length 5 exceeded"); var v1 = violations.get(1); assertThat(v1.path()).isEqualTo("address.line1"); @@ -72,7 +72,7 @@ void validateNested() { var v2 = violations.get(2); assertThat(v2.path()).isEqualTo("address.line2"); assertThat(v2.field()).isEqualTo("line2"); - assertThat(v2.message()).isEqualTo("size must be between 0 and 4"); + assertThat(v2.message()).isEqualTo("maximum length 4 exceeded"); var v3 = violations.get(3); assertThat(v3.path()).isEqualTo("address.longValue"); diff --git a/blackbox-test/src/test/java/example/jakarta/JCustomerMessageTest.java b/blackbox-test/src/test/java/example/jakarta/JCustomerMessageTest.java index 2a572992..5815a2f6 100644 --- a/blackbox-test/src/test/java/example/jakarta/JCustomerMessageTest.java +++ b/blackbox-test/src/test/java/example/jakarta/JCustomerMessageTest.java @@ -36,25 +36,25 @@ void blankDE() { @Test void sizeMax() { var violation = one(new JCustomer("NameIsTooLarge", "Other")); - assertThat(violation.message()).isEqualTo("size must be between 0 and 5"); + assertThat(violation.message()).isEqualTo("maximum length 5 exceeded"); } @Test void sizeMaxDE() { var violation = one(new JCustomer("NameIsTooLarge", "Other"), Locale.GERMAN); - assertThat(violation.message()).isEqualTo("Größe muss zwischen 0 und 5 sein"); + assertThat(violation.message()).isEqualTo("Länge muss zwischen 0 und 5 sein"); } @Test void sizeMinMax() { var violation = one(new JCustomer("valid", "Other", "TooLarge")); - assertThat(violation.message()).isEqualTo("size must be between 2 and 4"); + assertThat(violation.message()).isEqualTo("length must be between 2 and 4"); } @Test void sizeMinMaxDE() { var violation = one(new JCustomer("valid", "Other", "TooLarge"), Locale.GERMAN); - assertThat(violation.message()).isEqualTo("Größe muss zwischen 2 und 4 sein"); + assertThat(violation.message()).isEqualTo("Länge muss zwischen 2 und 4 sein"); } @Test diff --git a/validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java b/validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java index b207a50e..00445fdb 100644 --- a/validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java +++ b/validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java @@ -164,6 +164,8 @@ interface AdapterCreateRequest { Message message(); + Message message(String key); + String targetType(); AdapterCreateRequest withValue(long value); diff --git a/validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java b/validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java index 803918d7..b3c1d6cb 100644 --- a/validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java +++ b/validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java @@ -132,5 +132,12 @@ public Request withValue(long value) { public ValidationContext.Message message() { return ctx.message(attributes); } + + @Override + public ValidationContext.Message message(String messageKey) { + Map newAttributes = new HashMap<>(attributes); + newAttributes.put("message", messageKey); + return ctx.message(newAttributes); + } } } diff --git a/validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java b/validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java index 6f4d1a3f..1a82ee7d 100644 --- a/validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java +++ b/validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java @@ -65,16 +65,36 @@ public boolean isValid(CharSequence value) { private static final class SizeAdapter implements ValidationAdapter { + private static final String LENGTH = "{avaje.Length.message}"; + private static final String LENGTH_MAX = "{avaje.Length.max.message}"; + private static final String SIZE = "{avaje.Size.message}"; + private static final String SIZE_MAX = "{avaje.Size.max.message}"; private final ValidationContext.Message message; private final Set> groups; private final int min; private final int max; SizeAdapter(AdapterCreateRequest request) { - this.message = request.message(); this.groups = request.groups(); this.min = (int) request.attribute("min"); this.max = (int) request.attribute("max"); + + final Object msgKey = request.attribute("message"); + if (min == 0 && LENGTH.equals(msgKey)) { + this.message = request.message(LENGTH_MAX); + } else if (min == 0 && SIZE.equals(msgKey)) { + this.message = request.message(useLength(request) ? LENGTH_MAX : SIZE_MAX); + } else if (SIZE.equals(msgKey) && useLength(request)) { + this.message = request.message(LENGTH); + } else { + this.message = request.message(); + } + } + + /** Use 'Length' rather than 'Size' for string types */ + private static boolean useLength(AdapterCreateRequest request) { + final String targetType = request.targetType(); + return "String".equals(targetType) || "CharSequence".equals(targetType); } @Override diff --git a/validator/src/main/resources/io/avaje/validation/Messages.properties b/validator/src/main/resources/io/avaje/validation/Messages.properties index e3d9fd91..84f72925 100644 --- a/validator/src/main/resources/io/avaje/validation/Messages.properties +++ b/validator/src/main/resources/io/avaje/validation/Messages.properties @@ -22,8 +22,10 @@ avaje.Pattern.message = must match "{regexp}" avaje.Positive.message = must be greater than 0 avaje.PositiveOrZero.message = must be greater than or equal to 0 avaje.Size.message = size must be between {min} and {max} +avaje.Size.max.message = {avaje.Size.message} avaje.Length.message = length must be between {min} and {max} +avaje.Length.max.message = {avaje.Length.message} avaje.Range.message = must be between {min} and {max} avaje.URI.message = must be a valid URI avaje.UUID.message = must be a valid UUID diff --git a/validator/src/main/resources/io/avaje/validation/Messages_en.properties b/validator/src/main/resources/io/avaje/validation/Messages_en.properties index 4dcb972b..3c66c064 100644 --- a/validator/src/main/resources/io/avaje/validation/Messages_en.properties +++ b/validator/src/main/resources/io/avaje/validation/Messages_en.properties @@ -1 +1,3 @@ ## Intentionally blank +avaje.Length.max.message = maximum length {max} exceeded +avaje.Size.max.message = maximum size {max} exceeded