Skip to content

Commit 455c946

Browse files
authored
Merge pull request #63 from SentryMan/composite
Composable Constraints
2 parents 9123def + 263c709 commit 455c946

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+608
-89
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package example.avaje.composable;
2+
3+
import static java.lang.annotation.ElementType.TYPE;
4+
import static java.lang.annotation.RetentionPolicy.SOURCE;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
import io.avaje.validation.constraints.Constraint;
10+
import io.avaje.validation.constraints.Digits;
11+
import io.avaje.validation.constraints.Negative;
12+
import io.avaje.validation.constraints.Positive;
13+
14+
@Digits(integer = 2)
15+
@Constraint
16+
@Retention(SOURCE)
17+
@Target(TYPE)
18+
public @interface DigitsContraint {
19+
20+
String message() default "";
21+
22+
Class<?>[] groups() default {};
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package example.avaje.composable;
2+
3+
import static java.lang.annotation.ElementType.TYPE;
4+
import static java.lang.annotation.RetentionPolicy.SOURCE;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
import io.avaje.validation.constraints.Constraint;
10+
import io.avaje.validation.constraints.Positive;
11+
12+
@Positive
13+
@Constraint
14+
@Target(TYPE)
15+
@Retention(SOURCE)
16+
@DigitsContraint
17+
public @interface PositiveContraint {
18+
19+
String message() default "";
20+
21+
Class<?>[] groups() default {};
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package example.avaje.composable;
2+
3+
import jakarta.validation.Valid;
4+
5+
@Valid
6+
public record Sans(
7+
@SansPositiveContraint(message = "must have positive double digit amount of puns") int puns) {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package example.avaje.composable;
2+
3+
import static java.lang.annotation.ElementType.FIELD;
4+
import static java.lang.annotation.ElementType.TYPE;
5+
import static java.lang.annotation.RetentionPolicy.SOURCE;
6+
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.Target;
9+
10+
import jakarta.validation.Constraint;
11+
12+
@Retention(SOURCE)
13+
@Target(FIELD)
14+
@Constraint(validatedBy = {})
15+
@PositiveContraint(message = "ignored message")
16+
public @interface SansPositiveContraint {
17+
18+
String message();
19+
20+
Class<?>[] groups() default {};
21+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package example.avaje.composable;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.fail;
5+
6+
import java.util.Set;
7+
8+
import org.junit.jupiter.api.Test;
9+
10+
import io.avaje.validation.ConstraintViolation;
11+
import io.avaje.validation.ConstraintViolationException;
12+
import io.avaje.validation.Validator;
13+
14+
class SansComposableTest {
15+
16+
final Validator validator = Validator.builder().build();
17+
18+
@Test
19+
void valid() {
20+
final var undertale = new Sans(10);
21+
validator.validate(undertale);
22+
}
23+
24+
@Test
25+
void invalid() {
26+
var violations = violations(new Sans(-10));
27+
28+
violations.forEach(
29+
v -> assertThat(v.message()).isEqualTo("must have positive double digit amount of puns"));
30+
31+
violations = violations(new Sans(-420));
32+
33+
violations.forEach(
34+
v -> assertThat(v.message()).isEqualTo("must have positive double digit amount of puns"));
35+
}
36+
37+
Set<ConstraintViolation> violations(Object any) {
38+
try {
39+
validator.validate(any);
40+
fail("not expected");
41+
return null;
42+
} catch (final ConstraintViolationException e) {
43+
return e.violations();
44+
}
45+
}
46+
}

validator-constraints/pom.xml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<project xmlns="http://maven.apache.org/POM/4.0.0"
3-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5-
<modelVersion>4.0.0</modelVersion>
6-
<parent>
7-
<groupId>io.avaje</groupId>
8-
<artifactId>avaje-validator-parent</artifactId>
9-
<version>0.1-SNAPSHOT</version>
10-
</parent>
11-
<artifactId>validator-constraints</artifactId>
12-
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.avaje</groupId>
8+
<artifactId>avaje-validator-parent</artifactId>
9+
<version>0.1-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>validator-constraints</artifactId>
12+
<dependencies>
13+
<dependency>
14+
<groupId>io.avaje</groupId>
15+
<artifactId>avaje-validator</artifactId>
16+
<version>${project.version}</version>
17+
<optional>true</optional>
18+
</dependency>
19+
</dependencies>
1320
</project>
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68

79
import java.lang.annotation.Documented;
@@ -11,18 +13,19 @@
1113
import java.lang.annotation.RetentionPolicy;
1214
import java.lang.annotation.Target;
1315

14-
@Target({METHOD, FIELD, TYPE_USE})
16+
@Constraint
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RetentionPolicy.RUNTIME)
1619
@Repeatable(AssertFalse.List.class)
1720
public @interface AssertFalse {
18-
String message() default "{avaje.AssertFalse.message}";
21+
String message() default "{avaje.AssertFalse.message}";
1922

20-
Class<?>[] groups() default {};
23+
Class<?>[] groups() default {};
2124

22-
@Target({ElementType.METHOD, ElementType.FIELD})
23-
@Retention(RetentionPolicy.RUNTIME)
24-
@Documented
25-
@interface List {
26-
AssertFalse[] value();
27-
}
25+
@Target({ElementType.METHOD, ElementType.FIELD})
26+
@Retention(RetentionPolicy.RUNTIME)
27+
@Documented
28+
@interface List {
29+
AssertFalse[] value();
30+
}
2831
}
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68

79
import java.lang.annotation.Documented;
@@ -11,18 +13,19 @@
1113
import java.lang.annotation.RetentionPolicy;
1214
import java.lang.annotation.Target;
1315

14-
@Target({METHOD, FIELD, TYPE_USE})
16+
@Constraint
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RetentionPolicy.RUNTIME)
1619
@Repeatable(AssertTrue.List.class)
1720
public @interface AssertTrue {
18-
String message() default "{avaje.AssertTrue.message}";
21+
String message() default "{avaje.AssertTrue.message}";
1922

20-
Class<?>[] groups() default {};
23+
Class<?>[] groups() default {};
2124

22-
@Target({ElementType.METHOD, ElementType.FIELD})
23-
@Retention(RetentionPolicy.RUNTIME)
24-
@Documented
25-
@interface List {
26-
AssertTrue[] value();
27-
}
25+
@Target({ElementType.METHOD, ElementType.FIELD})
26+
@Retention(RetentionPolicy.RUNTIME)
27+
@Documented
28+
@interface List {
29+
AssertTrue[] value();
30+
}
2831
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.avaje.validation.constraints;
2+
3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4+
import static java.lang.annotation.RetentionPolicy.CLASS;
5+
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.Target;
8+
9+
/** Marks an annotation class as a Constraint. */
10+
@Retention(CLASS)
11+
@Target({ANNOTATION_TYPE})
12+
public @interface Constraint {}

validator-constraints/src/main/java/io/avaje/validation/constraints/DecimalMax.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68
import static java.lang.annotation.RetentionPolicy.RUNTIME;
79

@@ -11,7 +13,8 @@
1113

1214
import io.avaje.validation.constraints.DecimalMax.List;
1315

14-
@Target({METHOD, FIELD, TYPE_USE})
16+
@Constraint
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RUNTIME)
1619
@Repeatable(List.class)
1720
public @interface DecimalMax {

validator-constraints/src/main/java/io/avaje/validation/constraints/DecimalMin.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
*/
77
package io.avaje.validation.constraints;
88

9+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
910
import static java.lang.annotation.ElementType.FIELD;
1011
import static java.lang.annotation.ElementType.METHOD;
12+
import static java.lang.annotation.ElementType.PARAMETER;
13+
import static java.lang.annotation.ElementType.TYPE_USE;
1114
import static java.lang.annotation.RetentionPolicy.RUNTIME;
1215

1316
import java.lang.annotation.Repeatable;
@@ -36,7 +39,8 @@
3639
*
3740
* @author Emmanuel Bernard
3841
*/
39-
@Target({METHOD, FIELD})
42+
@Constraint
43+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
4044
@Retention(RUNTIME)
4145
@Repeatable(List.class)
4246
public @interface DecimalMin {

validator-constraints/src/main/java/io/avaje/validation/constraints/Digits.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68

79
import java.lang.annotation.Documented;
@@ -11,7 +13,8 @@
1113
import java.lang.annotation.RetentionPolicy;
1214
import java.lang.annotation.Target;
1315

14-
@Target({METHOD, FIELD, TYPE_USE})
16+
@Constraint
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RetentionPolicy.RUNTIME)
1619
@Repeatable(Digits.List.class)
1720
public @interface Digits {

validator-constraints/src/main/java/io/avaje/validation/constraints/Email.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68
import static java.lang.annotation.RetentionPolicy.RUNTIME;
79

@@ -18,7 +20,8 @@
1820
*
1921
* <p>Accepts {@code CharSequence}. {@code null} elements are considered valid.
2022
*/
21-
@Target({METHOD, FIELD, TYPE_USE})
23+
@Constraint
24+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
2225
@Retention(RUNTIME)
2326
@Repeatable(List.class)
2427
@Documented

validator-constraints/src/main/java/io/avaje/validation/constraints/Future.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68
import static java.lang.annotation.RetentionPolicy.RUNTIME;
79

@@ -10,8 +12,9 @@
1012
import java.lang.annotation.Retention;
1113
import java.lang.annotation.Target;
1214

15+
@Constraint
1316
@Documented
14-
@Target({METHOD, FIELD, TYPE_USE})
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RUNTIME)
1619
@Repeatable(Future.List.class)
1720
public @interface Future {

validator-constraints/src/main/java/io/avaje/validation/constraints/FutureOrPresent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68
import static java.lang.annotation.RetentionPolicy.RUNTIME;
79

@@ -10,8 +12,9 @@
1012
import java.lang.annotation.Retention;
1113
import java.lang.annotation.Target;
1214

15+
@Constraint
1316
@Documented
14-
@Target({METHOD, FIELD, TYPE_USE})
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RUNTIME)
1619
@Repeatable(FutureOrPresent.List.class)
1720
public @interface FutureOrPresent {

validator-constraints/src/main/java/io/avaje/validation/constraints/Max.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package io.avaje.validation.constraints;
22

3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
34
import static java.lang.annotation.ElementType.FIELD;
45
import static java.lang.annotation.ElementType.METHOD;
6+
import static java.lang.annotation.ElementType.PARAMETER;
57
import static java.lang.annotation.ElementType.TYPE_USE;
68

79
import java.lang.annotation.Documented;
@@ -11,7 +13,8 @@
1113
import java.lang.annotation.RetentionPolicy;
1214
import java.lang.annotation.Target;
1315

14-
@Target({METHOD, FIELD, TYPE_USE})
16+
@Constraint
17+
@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER, TYPE_USE})
1518
@Retention(RetentionPolicy.RUNTIME)
1619
@Repeatable(Max.List.class)
1720
public @interface Max {

0 commit comments

Comments
 (0)