Skip to content

Commit ed55ebe

Browse files
authored
Merge pull request #110 from avaje/feature/fix-classlevel
Fix for class level validation (recursive adapter creation)
2 parents f3f6a80 + 23fa528 commit ed55ebe

File tree

6 files changed

+119
-4
lines changed

6 files changed

+119
-4
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package example.avaje.crossfield;
2+
3+
import io.avaje.validation.constraints.Constraint;
4+
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.Target;
7+
8+
import static java.lang.annotation.ElementType.TYPE;
9+
import static java.lang.annotation.RetentionPolicy.SOURCE;
10+
11+
@Target(TYPE)
12+
@Retention(SOURCE)
13+
@Constraint
14+
public @interface APassingSkill {
15+
String message() default "put these foolish ambitions to rest"; // default error message
16+
17+
Class<?>[] groups() default {}; // groups
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package example.avaje.crossfield;
2+
3+
import io.avaje.validation.adapter.AbstractConstraintAdapter;
4+
import io.avaje.validation.adapter.ConstraintAdapter;
5+
import io.avaje.validation.adapter.ValidationContext.AdapterCreateRequest;
6+
7+
@ConstraintAdapter(APassingSkill.class)
8+
public final class APassingSkillAdapter extends AbstractConstraintAdapter<ATarnished> {
9+
10+
public APassingSkillAdapter(AdapterCreateRequest request) {
11+
super(request);
12+
}
13+
14+
@Override
15+
public boolean isValid(ATarnished lowlyTarnished) {
16+
if (lowlyTarnished == null) {
17+
return true;
18+
}
19+
return lowlyTarnished.vigor() >= 50 && lowlyTarnished.endurance() >= 50;
20+
}
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package example.avaje.crossfield;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.Positive;
5+
import io.avaje.validation.constraints.Valid;
6+
7+
@Valid
8+
@APassingSkill
9+
public record ATarnished(
10+
@NotBlank String name,
11+
@Positive int vigor,
12+
@Positive int endurance) {
13+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package example.avaje.crossfield;
2+
3+
import io.avaje.validation.ConstraintViolation;
4+
import io.avaje.validation.ConstraintViolationException;
5+
import io.avaje.validation.Validator;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.util.ArrayList;
9+
10+
import static org.assertj.core.api.Assertions.assertThat;
11+
import static org.assertj.core.api.Assertions.fail;
12+
13+
class ATarnishedTest {
14+
15+
Validator validator = Validator.builder().build();
16+
17+
@Test
18+
void valid() {
19+
validator.validate(new ATarnished("ok", 50, 50));
20+
}
21+
22+
@Test
23+
void invalid_classLevelValidation() {
24+
var violation = one(new ATarnished("ok", 49, 50));
25+
assertThat(violation.message()).isEqualTo("put these foolish ambitions to rest");
26+
assertThat(violation.path()).isEqualTo("null");
27+
assertThat(violation.field()).isNull();
28+
}
29+
30+
31+
@Test
32+
void invalidField_expect_classValidationToNotRun() {
33+
var violation = one(new ATarnished(" ", 49, 50));
34+
assertThat(violation.message()).isEqualTo("must not be blank");
35+
assertThat(violation.path()).isEqualTo("name");
36+
assertThat(violation.field()).isEqualTo("name");
37+
}
38+
39+
@Test
40+
void invalidField2_expect_classValidationToNotRun() {
41+
var violation = one(new ATarnished("ok", -1, 50));
42+
assertThat(violation.message()).isEqualTo("must be greater than 0");
43+
assertThat(violation.path()).isEqualTo("vigor");
44+
assertThat(violation.field()).isEqualTo("vigor");
45+
}
46+
47+
ConstraintViolation one(Object any) {
48+
try {
49+
validator.validate(any);
50+
fail("not expected");
51+
return null;
52+
} catch (ConstraintViolationException e) {
53+
var violations = new ArrayList<>(e.violations());
54+
assertThat(violations).hasSize(1);
55+
return violations.get(0);
56+
}
57+
}
58+
}

validator-generator/src/main/java/io/avaje/validation/generator/AdapterHelper.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,20 @@ class AdapterHelper {
1212
private final String type;
1313
private final GenericType topType;
1414
private final GenericType genericType;
15+
private final boolean classLevel;
1516

1617
AdapterHelper(Append writer, ElementAnnotationContainer elementAnnotations, String indent) {
17-
this(writer, elementAnnotations, indent,"Object", null);
18+
this(writer, elementAnnotations, indent,"Object", null, false);
1819
}
1920

20-
AdapterHelper(Append writer, ElementAnnotationContainer elementAnnotations, String indent, String type, GenericType topType) {
21+
AdapterHelper(Append writer, ElementAnnotationContainer elementAnnotations, String indent, String type, GenericType topType, boolean classLevel) {
2122
this.writer = writer;
2223
this.elementAnnotations = elementAnnotations;
2324
this.indent = indent;
2425
this.type = type;
2526
this.topType = topType;
2627
this.genericType = elementAnnotations.genericType();
28+
this.classLevel = classLevel;
2729
}
2830

2931
void write() {
@@ -70,7 +72,9 @@ void write() {
7072
writeTypeUse(genericType.firstParamType(), typeUse1);
7173

7274
} else if (hasValid) {
73-
writer.eol().append("%s .andThen(ctx.adapter(%s.class))", indent, Util.shortName(genericType.topType()));
75+
if (!classLevel) {
76+
writer.eol().append("%s .andThen(ctx.adapter(%s.class))", indent, Util.shortName(genericType.topType()));
77+
}
7478

7579
} else if (genericType.topType().contains("java.util.Optional")) {
7680
writer.eol().append("%s .optional()", indent);

validator-generator/src/main/java/io/avaje/validation/generator/FieldReader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ public void writeConstructor(Append writer) {
182182
elementAnnotations,
183183
" ",
184184
PrimitiveUtil.wrap(genericType.shortType()),
185-
genericType)
185+
genericType,
186+
classLevel)
186187
.write();
187188
writer.append(";").eol().eol();
188189
}

0 commit comments

Comments
 (0)