Skip to content

Commit 5e87706

Browse files
SentryManrbygrave
andauthored
Fix Recursive Validation (#256)
* fix recursive validation * Delete validator-generator/io.avaje.validation.spi.ValidationExtension * Update Recursive.java * Format, remove unneeded public modifier --------- Co-authored-by: Rob Bygrave <[email protected]>
1 parent 72c61c7 commit 5e87706

File tree

5 files changed

+83
-16
lines changed

5 files changed

+83
-16
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotNull;
4+
import io.avaje.validation.constraints.Valid;
5+
6+
@Valid
7+
public record Recursive(@NotNull String name, @Valid @NotNull Recursive child) {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package example.avaje.cascade;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import io.avaje.validation.Validator;
8+
9+
class RecursiveTest {
10+
11+
Validator validator = Validator.builder().build();
12+
13+
@Test
14+
void valid() {
15+
var recurse =
16+
new Recursive("recursive1", new Recursive("recursive2", new Recursive("recursive3", null)));
17+
assertThat(validator.check(recurse).iterator().next())
18+
.matches(c -> "child.child.child".equals(c.path()));
19+
}
20+
}

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

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ final class AdapterHelper {
1515
private final boolean classLevel;
1616
private final boolean crossParam;
1717
private boolean usePrimitiveValidation;
18+
private String recursiveType;
1819

1920
AdapterHelper(Append writer, ElementAnnotationContainer elementAnnotations, String indent) {
20-
this(writer, elementAnnotations, indent,"Object", null, false, false);
21+
this(writer, elementAnnotations, indent, "Object", null, false, false);
2122
}
2223

2324
AdapterHelper(
@@ -72,25 +73,39 @@ void write() {
7273

7374
} else if (isTopTypeIterable()) {
7475
writer.eol().append("%s .list()", indent);
76+
// cascade validate
7577
if (hasValid) {
76-
// cascade validate
77-
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class))", indent, mainType.param0().shortType());
78+
if (mainType.param0().fullWithoutAnnotations().equals(recursiveType)) {
79+
// cascade validate
80+
writer.eol().append("%s .andThenMulti(this)", indent, mainType.param0().shortType());
81+
} else {
82+
// cascade validate
83+
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class))", indent, mainType.param0().shortType());
84+
}
7885
}
7986

8087
} else if (isMapType(typeUse1, typeUse2)) {
8188
writer.eol().append("%s .mapKeys()", indent);
8289
writeTypeUse(genericType.param0(), typeUse1);
83-
8490
writer.eol().append("%s .mapValues()", indent);
8591
writeTypeUse(genericType.param1(), typeUse2, false);
8692

8793
} else if (hasValid && genericType.mainType().contains("[]")) {
8894
writer.eol().append("%s .array()", indent);
89-
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class))", indent, mainType.shortWithoutAnnotations().replace("[]",""));
95+
if (genericType.mainType().replace("[]", "").equals(recursiveType)) {
96+
writer.eol().append("%s .andThenMulti(this)", indent);
97+
} else {
98+
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class))",
99+
indent, mainType.shortWithoutAnnotations().replace("[]", ""));
100+
}
90101

91102
} else if (hasValid) {
92103
if (!classLevel) {
93-
writer.eol().append("%s .andThen(ctx.adapter(%s.class))", indent, genericType.shortWithoutAnnotations());
104+
if (genericType.mainType().equals(recursiveType)) {
105+
writer.eol().append("%s .andThen(this)", indent);
106+
} else {
107+
writer.eol().append("%s .andThen(ctx.adapter(%s.class))", indent, genericType.shortWithoutAnnotations());
108+
}
94109
}
95110

96111
} else if (genericType.mainType().contains("java.util.Optional")) {
@@ -147,4 +162,8 @@ private void writeTypeUse(UType uType, Map<UType, String> typeUseMap, boolean ke
147162
.append("%s .andThenMulti(ctx.adapter(%s.class))", indent, typeUse.shortWithoutAnnotations());
148163
}
149164
}
165+
166+
void withEnclosingType(UType recursive) {
167+
this.recursiveType = recursive.fullWithoutAnnotations();
168+
}
150169
}

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package io.avaje.validation.generator;
22

3-
import static io.avaje.validation.generator.PrimitiveUtil.isPrimitiveValidationType;
43
import static io.avaje.validation.generator.APContext.logError;
4+
import static io.avaje.validation.generator.PrimitiveUtil.isPrimitiveValidationType;
55

66
import java.util.List;
7+
import java.util.Optional;
78
import java.util.Set;
89

910
import javax.lang.model.element.Element;
@@ -174,15 +175,22 @@ public String toString() {
174175
void writeConstructor(Append writer) {
175176
writer.append(" this.%s = ", adapterFieldName).eol();
176177

177-
new AdapterHelper(
178-
writer,
179-
elementAnnotations,
180-
" ",
181-
PrimitiveUtil.wrap(genericType.shortWithoutAnnotations()),
182-
genericType,
183-
classLevel)
184-
.usePrimitiveValidation(usePrimitiveValidation)
185-
.write();
178+
var helper =
179+
new AdapterHelper(
180+
writer,
181+
elementAnnotations,
182+
" ",
183+
PrimitiveUtil.wrap(genericType.shortWithoutAnnotations()),
184+
genericType,
185+
classLevel)
186+
.usePrimitiveValidation(usePrimitiveValidation);
187+
188+
Optional.of(element.getEnclosingElement())
189+
.filter(TypeElement.class::isInstance)
190+
.map(e -> UType.parse(e.asType()))
191+
.ifPresent(helper::withEnclosingType);
192+
193+
helper.write();
186194
writer.append(";").eol().eol();
187195
}
188196

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.avaje.validation.generator.models.valid;
2+
3+
import java.util.List;
4+
5+
import io.avaje.validation.constraints.NotNull;
6+
import io.avaje.validation.constraints.Valid;
7+
8+
@Valid
9+
public record Recursive(
10+
@NotNull String name,
11+
@Valid @NotNull Recursive child,
12+
@Valid Recursive[] array,
13+
@Valid List<Recursive> list) {}

0 commit comments

Comments
 (0)