Skip to content

Commit 3afdf7e

Browse files
committed
feat(validate): Array validation with items/minItems/maxItems/uniqueItems
1 parent 0afd3a7 commit 3afdf7e

2 files changed

Lines changed: 101 additions & 1 deletion

File tree

src/main/java/com/retailsvc/http/validate/DefaultValidator.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
import com.retailsvc.http.spec.schema.TypeName;
2121
import java.time.LocalDate;
2222
import java.time.OffsetDateTime;
23+
import java.util.ArrayList;
24+
import java.util.HashSet;
25+
import java.util.List;
2326
import java.util.Map;
2427
import java.util.Objects;
28+
import java.util.Set;
2529
import java.util.UUID;
2630
import java.util.function.Function;
2731
import java.util.regex.Pattern;
@@ -196,7 +200,26 @@ private void validateObject(Object value, ObjectSchema s, String pointer) {
196200
}
197201

198202
private void validateArray(Object value, ArraySchema s, String pointer) {
199-
throw new UnsupportedOperationException("E5 implements array");
203+
require(value instanceof Iterable, pointer, "type", "expected array");
204+
Iterable<?> it = (Iterable<?>) value;
205+
List<Object> elements = new ArrayList<>();
206+
for (Object o : it) elements.add(o);
207+
208+
if (s.minItems() != null && elements.size() < s.minItems())
209+
fail(pointer, "minItems", "fewer than " + s.minItems() + " items", elements.size());
210+
if (s.maxItems() != null && elements.size() > s.maxItems())
211+
fail(pointer, "maxItems", "more than " + s.maxItems() + " items", elements.size());
212+
213+
if (s.uniqueItems()) {
214+
Set<Object> seen = new HashSet<>();
215+
for (Object e : elements) {
216+
if (!seen.add(e)) fail(pointer, "uniqueItems", "duplicate item", e);
217+
}
218+
}
219+
220+
for (int i = 0; i < elements.size(); i++) {
221+
validate(elements.get(i), s.items(), pointer + "/" + i);
222+
}
200223
}
201224

202225
private static void fail(String pointer, String keyword, String message, Object rejectedValue) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.retailsvc.http.validate;
2+
3+
import static org.assertj.core.api.Assertions.assertThatCode;
4+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
6+
import com.retailsvc.http.ValidationException;
7+
import com.retailsvc.http.spec.schema.ArraySchema;
8+
import com.retailsvc.http.spec.schema.BooleanSchema;
9+
import com.retailsvc.http.spec.schema.IntegerSchema;
10+
import com.retailsvc.http.spec.schema.Schema;
11+
import com.retailsvc.http.spec.schema.TypeName;
12+
import java.util.List;
13+
import java.util.Set;
14+
import org.junit.jupiter.api.Test;
15+
16+
class ArrayValidationTest {
17+
private final Validator v =
18+
new DefaultValidator(
19+
name -> {
20+
throw new AssertionError();
21+
});
22+
23+
private ArraySchema arr(Schema item, Integer minI, Integer maxI, boolean unique) {
24+
return new ArraySchema(Set.of(TypeName.ARRAY), item, minI, maxI, unique);
25+
}
26+
27+
@Test
28+
void itemsValidated() {
29+
var s =
30+
arr(
31+
new IntegerSchema(Set.of(TypeName.INTEGER), 0L, 100L, null, null, null, "int32"),
32+
null,
33+
null,
34+
false);
35+
assertThatCode(() -> v.validate(List.of(1, 2, 3), s, "")).doesNotThrowAnyException();
36+
assertThatThrownBy(() -> v.validate(List.of(1, -1), s, ""))
37+
.extracting(t -> ((ValidationException) t).error().pointer())
38+
.isEqualTo("/1");
39+
}
40+
41+
@Test
42+
void minItemsEnforced() {
43+
var s = arr(new BooleanSchema(Set.of(TypeName.BOOLEAN)), 2, null, false);
44+
assertThatThrownBy(() -> v.validate(List.of(true), s, ""))
45+
.extracting(t -> ((ValidationException) t).error().keyword())
46+
.isEqualTo("minItems");
47+
}
48+
49+
@Test
50+
void maxItemsEnforced() {
51+
var s = arr(new BooleanSchema(Set.of(TypeName.BOOLEAN)), null, 1, false);
52+
assertThatThrownBy(() -> v.validate(List.of(true, false), s, ""))
53+
.extracting(t -> ((ValidationException) t).error().keyword())
54+
.isEqualTo("maxItems");
55+
}
56+
57+
@Test
58+
void uniqueItemsEnforced() {
59+
var s =
60+
arr(
61+
new IntegerSchema(Set.of(TypeName.INTEGER), null, null, null, null, null, "int32"),
62+
null,
63+
null,
64+
true);
65+
assertThatThrownBy(() -> v.validate(List.of(1, 2, 1), s, ""))
66+
.extracting(t -> ((ValidationException) t).error().keyword())
67+
.isEqualTo("uniqueItems");
68+
}
69+
70+
@Test
71+
void rejectsNonIterable() {
72+
var s = arr(new BooleanSchema(Set.of(TypeName.BOOLEAN)), null, null, false);
73+
assertThatThrownBy(() -> v.validate("nope", s, "/v"))
74+
.extracting(t -> ((ValidationException) t).error().keyword())
75+
.isEqualTo("type");
76+
}
77+
}

0 commit comments

Comments
 (0)