Skip to content

Commit d0d0598

Browse files
authored
Merge pull request #114 from avaje/feature/cascade-array-type
Tests and fixes for cascading array of pojo
2 parents d5ad073 + 5f6fa98 commit d0d0598

File tree

10 files changed

+165
-7
lines changed

10 files changed

+165
-7
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.Valid;
5+
6+
import java.util.Set;
7+
8+
@Valid
9+
public record BShip(
10+
@NotBlank String name,
11+
@Valid Set<ACrew> crew // cascade validation
12+
) { }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.Valid;
5+
6+
@Valid
7+
public record CShip(
8+
@NotBlank String name,
9+
@Valid ACrew[] crew // cascade validation
10+
) {
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.NotEmpty;
5+
import io.avaje.validation.constraints.Valid;
6+
7+
@Valid
8+
public record CShip2(
9+
@NotBlank String name,
10+
@Valid @NotEmpty ACrew[] crew // cascade validation
11+
) {
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.NotEmpty;
5+
import io.avaje.validation.constraints.Valid;
6+
7+
@Valid
8+
public record CShip3(
9+
@NotBlank String name,
10+
@NotEmpty ACrew[] crew // NotEmpty with no cascade
11+
) {
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package example.avaje.cascade;
2+
3+
import io.avaje.validation.constraints.NotBlank;
4+
import io.avaje.validation.constraints.NotEmpty;
5+
import io.avaje.validation.constraints.Valid;
6+
7+
@Valid
8+
public record DShip(
9+
@NotBlank String name,
10+
@NotEmpty String[] crew // cascade validation
11+
) {
12+
}

blackbox-test/src/test/java/example/avaje/cascade/AShipTest.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.ArrayList;
99
import java.util.List;
1010
import java.util.Locale;
11+
import java.util.Set;
1112

1213
import static org.assertj.core.api.Assertions.assertThat;
1314
import static org.assertj.core.api.Assertions.fail;
@@ -22,6 +23,30 @@ void valid() {
2223
validator.validate(ship);
2324
}
2425

26+
@Test
27+
void valid_usingSet() {
28+
var ship = new BShip("lollyPop", Set.of(new ACrew("ok", null)));
29+
validator.validate(ship);
30+
}
31+
32+
@Test
33+
void valid_usingArray() {
34+
var ship = new CShip("lollyPop", new ACrew[]{new ACrew("ok", null)});
35+
validator.validate(ship);
36+
}
37+
38+
@Test
39+
void valid_usingArray3() {
40+
var ship = new CShip3("lollyPop", new ACrew[]{new ACrew("ok", null)});
41+
validator.validate(ship);
42+
}
43+
44+
@Test
45+
void valid_usingScalarArray() {
46+
var ship = new DShip("lollyPop", new String[]{"bob"});
47+
validator.validate(ship);
48+
}
49+
2550
@Test
2651
void validAllowEmptyCollection() {
2752
var ship = new AShip("lollyPop", List.of());
@@ -82,6 +107,72 @@ void invalidShip2_when_nullCollection_expect_InvalidEmptyCollection() {
82107
assertThat(violations.get(1).message()).isEqualTo("must not be empty");
83108
}
84109

110+
@Test
111+
void arrayCascade() {
112+
var ship = new CShip("", new ACrew[]{new ACrew("NotValid", null)});
113+
List<ConstraintViolation> violations = violations(ship);
114+
115+
assertThat(violations).hasSize(2);
116+
assertThat(violations.get(0).path()).isEqualTo("name");
117+
assertThat(violations.get(0).message()).isEqualTo("must not be blank");
118+
assertThat(violations.get(1).path()).isEqualTo("crew[0].name");
119+
assertThat(violations.get(1).message()).isEqualTo("maximum length 4 exceeded");
120+
}
121+
122+
@Test
123+
void arrayNotCascade() {
124+
var ship = new CShip3("", new ACrew[]{new ACrew("NotValid", null)});
125+
List<ConstraintViolation> violations = violations(ship);
126+
127+
assertThat(violations).hasSize(1);
128+
assertThat(violations.get(0).path()).isEqualTo("name");
129+
assertThat(violations.get(0).message()).isEqualTo("must not be blank");
130+
}
131+
132+
@Test
133+
void arrayNotEmpty_when_empty() {
134+
var ship = new CShip2("", new ACrew[]{});
135+
List<ConstraintViolation> violations = violations(ship);
136+
137+
assertThat(violations).hasSize(2);
138+
assertThat(violations.get(0).path()).isEqualTo("name");
139+
assertThat(violations.get(0).message()).isEqualTo("must not be blank");
140+
assertThat(violations.get(1).path()).isEqualTo("crew");
141+
assertThat(violations.get(1).message()).isEqualTo("must not be empty");
142+
}
143+
144+
@Test
145+
void arrayNotEmpty_when_null() {
146+
var ship = new CShip2("ok", null);
147+
List<ConstraintViolation> violations = violations(ship);
148+
149+
assertThat(violations).hasSize(1);
150+
assertThat(violations.get(0).path()).isEqualTo("crew");
151+
assertThat(violations.get(0).message()).isEqualTo("must not be empty");
152+
}
153+
154+
@Test
155+
void arrayNotEmpty_when_scalarArrayEmpty() {
156+
var ship = new DShip("", new String[]{});
157+
List<ConstraintViolation> violations = violations(ship);
158+
159+
assertThat(violations).hasSize(2);
160+
assertThat(violations.get(0).path()).isEqualTo("name");
161+
assertThat(violations.get(0).message()).isEqualTo("must not be blank");
162+
assertThat(violations.get(1).path()).isEqualTo("crew");
163+
assertThat(violations.get(1).message()).isEqualTo("must not be empty");
164+
}
165+
166+
@Test
167+
void arrayNotEmpty_when_scalarNull() {
168+
var ship = new DShip("ok", null);
169+
List<ConstraintViolation> violations = violations(ship);
170+
171+
assertThat(violations).hasSize(1);
172+
assertThat(violations.get(0).path()).isEqualTo("crew");
173+
assertThat(violations.get(0).message()).isEqualTo("must not be empty");
174+
}
175+
85176
List<ConstraintViolation> violations(Object any) {
86177
return violations(any, Locale.ENGLISH);
87178
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import static io.avaje.validation.generator.ProcessingContext.isAssignable2Interface;
66

7-
class AdapterHelper {
7+
final class AdapterHelper {
88

99
private final Append writer;
1010
private final ElementAnnotationContainer elementAnnotations;
@@ -67,9 +67,9 @@ void write() {
6767
writer.eol().append("%s .mapValues()", indent);
6868
writeTypeUse(genericType.secondParamType(), typeUse2, false);
6969

70-
} else if (genericType.topType().contains("[]") && hasValid) {
70+
} else if (hasValid && genericType.topType().contains("[]")) {
7171
writer.eol().append("%s .array()", indent);
72-
writeTypeUse(genericType.firstParamType(), typeUse1);
72+
writer.eol().append("%s .andThenMulti(ctx.adapter(%s.class))", indent, topType.shortNameMinusArray());
7373

7474
} else if (hasValid) {
7575
if (!classLevel) {

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public String toString() {
5050
}
5151

5252
void addImports(Set<String> importTypes) {
53-
final String type = trimExtends();
53+
final String type = trimExtends().replace("[]", "");
5454
if (includeInImports(type)) {
5555
importTypes.add(type);
5656
}
@@ -97,10 +97,18 @@ void writeType(String prefix, StringBuilder sb) {
9797
}
9898
}
9999

100+
String shortNameMinusArray() {
101+
return shortNameRaw().replace("[]", "");
102+
}
103+
100104
String shortName() {
105+
return shortNameRaw().replace("[]", "Array");
106+
}
107+
108+
String shortNameRaw() {
101109
final StringBuilder sb = new StringBuilder();
102110
shortName(sb);
103-
return sb.toString().replace("[]", "Array");
111+
return sb.toString();
104112
}
105113

106114
void shortName(StringBuilder sb) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ private void cascadeTypesInner() {
146146
}
147147

148148
private boolean cascadeElement(TypeElement element) {
149-
return element.getKind() != ElementKind.ENUM && !metaData.contains(adapterName(element));
149+
return element != null && element.getKind() != ElementKind.ENUM && !metaData.contains(adapterName(element));
150150
}
151151

152152
private String adapterName(TypeElement element) {

validator/src/main/java/io/avaje/validation/adapter/AbstractContainerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ protected boolean validateAll(Iterable<Object> value, ValidationRequest req, Str
4949

5050
/** Execute validations for all items in the given array */
5151
protected boolean validateArray(Object[] value, ValidationRequest req, String propertyName) {
52-
if (value == null) {
52+
if (value == null || multiAdapter == null) {
5353
return true;
5454
}
5555
if (propertyName != null) {

0 commit comments

Comments
 (0)