Skip to content

Commit 48ca3c2

Browse files
authored
Fix walk for if validator with validation (#1010)
1 parent 62ed35a commit 48ca3c2

File tree

2 files changed

+233
-11
lines changed

2 files changed

+233
-11
lines changed

src/main/java/com/networknt/schema/IfValidator.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,33 @@ public void preloadJsonSchema() {
102102

103103
@Override
104104
public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) {
105-
if (shouldValidateSchema) {
106-
return validate(executionContext, node, rootNode, instanceLocation);
107-
}
105+
boolean checkCondition = node != null && shouldValidateSchema;
106+
boolean ifConditionPassed = false;
108107

109-
if (null != this.ifSchema) {
110-
this.ifSchema.walk(executionContext, node, rootNode, instanceLocation, false);
111-
}
112-
if (null != this.thenSchema) {
113-
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, false);
108+
// Save flag as nested schema evaluation shouldn't trigger fail fast
109+
boolean failFast = executionContext.isFailFast();
110+
try {
111+
executionContext.setFailFast(false);
112+
ifConditionPassed = this.ifSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema).isEmpty();
113+
} finally {
114+
// Restore flag
115+
executionContext.setFailFast(failFast);
114116
}
115-
if (null != this.elseSchema) {
116-
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, false);
117+
if (!checkCondition) {
118+
if (this.thenSchema != null) {
119+
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
120+
}
121+
if (this.elseSchema != null) {
122+
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
123+
}
124+
} else {
125+
if (this.thenSchema != null && ifConditionPassed) {
126+
return this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
127+
}
128+
else if (this.elseSchema != null && !ifConditionPassed) {
129+
return this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
130+
}
117131
}
118-
119132
return Collections.emptySet();
120133
}
121134

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Copyright (c) 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.networknt.schema;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertFalse;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.Set;
25+
26+
import org.junit.jupiter.api.Test;
27+
28+
import com.networknt.schema.SpecVersion.VersionFlag;
29+
import com.networknt.schema.walk.JsonSchemaWalkListener;
30+
import com.networknt.schema.walk.WalkEvent;
31+
import com.networknt.schema.walk.WalkFlow;
32+
33+
/**
34+
* Test for IfValidator.
35+
*/
36+
class IfValidatorTest {
37+
38+
@Test
39+
void walkValidateThen() {
40+
String schemaData = "{\r\n"
41+
+ " \"if\": {\r\n"
42+
+ " \"const\": \"false\"\r\n"
43+
+ " },\r\n"
44+
+ " \"then\": {\r\n"
45+
+ " \"type\": \"object\"\r\n"
46+
+ " },\r\n"
47+
+ " \"else\": {\r\n"
48+
+ " \"type\": \"number\"\r\n"
49+
+ " }\r\n"
50+
+ "}";
51+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
52+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
53+
config.setPathType(PathType.JSON_POINTER);
54+
config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() {
55+
56+
@Override
57+
public WalkFlow onWalkStart(WalkEvent walkEvent) {
58+
return WalkFlow.CONTINUE;
59+
}
60+
61+
@Override
62+
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
63+
@SuppressWarnings("unchecked")
64+
List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext()
65+
.getCollectorContext()
66+
.getCollectorMap()
67+
.computeIfAbsent("types", key -> new ArrayList<JsonNodePath>());
68+
types.add(walkEvent);
69+
}
70+
});
71+
JsonSchema schema = factory.getSchema(schemaData, config);
72+
ValidationResult result = schema.walk("\"false\"", InputFormat.JSON, true);
73+
assertFalse(result.getValidationMessages().isEmpty());
74+
75+
@SuppressWarnings("unchecked")
76+
List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
77+
assertEquals(1, types.size());
78+
assertEquals("", types.get(0).getInstanceLocation().toString());
79+
assertEquals("/then", types.get(0).getSchema().getEvaluationPath().toString());
80+
}
81+
82+
@Test
83+
void walkValidateElse() {
84+
String schemaData = "{\r\n"
85+
+ " \"if\": {\r\n"
86+
+ " \"const\": \"false\"\r\n"
87+
+ " },\r\n"
88+
+ " \"then\": {\r\n"
89+
+ " \"type\": \"object\"\r\n"
90+
+ " },\r\n"
91+
+ " \"else\": {\r\n"
92+
+ " \"type\": \"number\"\r\n"
93+
+ " }\r\n"
94+
+ "}";
95+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
96+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
97+
config.setPathType(PathType.JSON_POINTER);
98+
config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() {
99+
100+
@Override
101+
public WalkFlow onWalkStart(WalkEvent walkEvent) {
102+
return WalkFlow.CONTINUE;
103+
}
104+
105+
@Override
106+
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
107+
@SuppressWarnings("unchecked")
108+
List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext()
109+
.getCollectorContext()
110+
.getCollectorMap()
111+
.computeIfAbsent("types", key -> new ArrayList<JsonNodePath>());
112+
types.add(walkEvent);
113+
}
114+
});
115+
JsonSchema schema = factory.getSchema(schemaData, config);
116+
ValidationResult result = schema.walk("\"hello\"", InputFormat.JSON, true);
117+
assertFalse(result.getValidationMessages().isEmpty());
118+
119+
@SuppressWarnings("unchecked")
120+
List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
121+
assertEquals(1, types.size());
122+
assertEquals("", types.get(0).getInstanceLocation().toString());
123+
assertEquals("/else", types.get(0).getSchema().getEvaluationPath().toString());
124+
}
125+
126+
@Test
127+
void walkValidateNull() {
128+
String schemaData = "{\r\n"
129+
+ " \"if\": {\r\n"
130+
+ " \"const\": \"false\"\r\n"
131+
+ " },\r\n"
132+
+ " \"then\": {\r\n"
133+
+ " \"type\": \"object\"\r\n"
134+
+ " },\r\n"
135+
+ " \"else\": {\r\n"
136+
+ " \"type\": \"number\"\r\n"
137+
+ " }\r\n"
138+
+ "}";
139+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
140+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
141+
config.setPathType(PathType.JSON_POINTER);
142+
config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() {
143+
144+
@Override
145+
public WalkFlow onWalkStart(WalkEvent walkEvent) {
146+
return WalkFlow.CONTINUE;
147+
}
148+
149+
@Override
150+
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
151+
@SuppressWarnings("unchecked")
152+
List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext()
153+
.getCollectorContext()
154+
.getCollectorMap()
155+
.computeIfAbsent("types", key -> new ArrayList<JsonNodePath>());
156+
types.add(walkEvent);
157+
}
158+
});
159+
JsonSchema schema = factory.getSchema(schemaData, config);
160+
ValidationResult result = schema.walk(null, true);
161+
assertTrue(result.getValidationMessages().isEmpty());
162+
163+
@SuppressWarnings("unchecked")
164+
List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
165+
assertEquals(2, types.size());
166+
}
167+
168+
@Test
169+
void walkNoValidate() {
170+
String schemaData = "{\r\n"
171+
+ " \"if\": {\r\n"
172+
+ " \"const\": \"false\"\r\n"
173+
+ " },\r\n"
174+
+ " \"then\": {\r\n"
175+
+ " \"type\": \"object\"\r\n"
176+
+ " },\r\n"
177+
+ " \"else\": {\r\n"
178+
+ " \"type\": \"number\"\r\n"
179+
+ " }\r\n"
180+
+ "}";
181+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012);
182+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
183+
config.setPathType(PathType.JSON_POINTER);
184+
config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() {
185+
186+
@Override
187+
public WalkFlow onWalkStart(WalkEvent walkEvent) {
188+
return WalkFlow.CONTINUE;
189+
}
190+
191+
@Override
192+
public void onWalkEnd(WalkEvent walkEvent, Set<ValidationMessage> validationMessages) {
193+
@SuppressWarnings("unchecked")
194+
List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext()
195+
.getCollectorContext()
196+
.getCollectorMap()
197+
.computeIfAbsent("types", key -> new ArrayList<JsonNodePath>());
198+
types.add(walkEvent);
199+
}
200+
});
201+
JsonSchema schema = factory.getSchema(schemaData, config);
202+
ValidationResult result = schema.walk("\"false\"", InputFormat.JSON, false);
203+
assertTrue(result.getValidationMessages().isEmpty());
204+
205+
@SuppressWarnings("unchecked")
206+
List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
207+
assertEquals(2, types.size());
208+
}
209+
}

0 commit comments

Comments
 (0)