Skip to content

Commit 5007242

Browse files
Adding Unevaluated properties keyword. (#534)
* Fixing walk issues for properties * Correcting walk changes for few validators * adding item walk listener * walk listener changes * walk listener changes * merging changes from networknt * Fixing issues with ValidatorState * changing the method name to getCollectorContext * correcting the variable names and ValidatorState logic * correcting the documentation logic * Fixing compilation issues in TypeFactoryTest * Fixing the minor brace issue * Fixing the issue of propogating walk-listeners to child 's from parent schema * Adding tests for changing listeners * Correcting the ref listeners config in walk event * Correcting the ref listeners config in walk event * Adding unevaluated properties * Fixing unevaluated properties * Fixing unevaluated properties * Fixing unevaluatedProperties issues Co-authored-by: Prashanth Josyula <[email protected]>
1 parent 4fb2eba commit 5007242

13 files changed

+1108
-125
lines changed

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

+44-28
Original file line numberDiff line numberDiff line change
@@ -52,39 +52,55 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5252

5353
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
5454

55+
// As AllOf might contain multiple schemas take a backup of evaluatedProperties.
56+
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
57+
58+
// Make the evaluatedProperties list empty.
59+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
60+
5561
for (JsonSchema schema : schemas) {
56-
errors.addAll(schema.validate(node, rootNode, at));
57-
58-
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
59-
final Iterator<JsonNode> arrayElements = schemaNode.elements();
60-
while (arrayElements.hasNext()) {
61-
final ObjectNode allOfEntry = (ObjectNode) arrayElements.next();
62-
final JsonNode $ref = allOfEntry.get("$ref");
63-
if (null != $ref) {
64-
final ValidationContext.DiscriminatorContext currentDiscriminatorContext = validationContext
65-
.getCurrentDiscriminatorContext();
66-
if (null != currentDiscriminatorContext) {
67-
final ObjectNode discriminator = currentDiscriminatorContext
68-
.getDiscriminatorForPath(allOfEntry.get("$ref").asText());
69-
if (null != discriminator) {
70-
registerAndMergeDiscriminator(currentDiscriminatorContext, discriminator, parentSchema, at);
71-
// now we have to check whether we have hit the right target
72-
final String discriminatorPropertyName = discriminator.get("propertyName").asText();
73-
final JsonNode discriminatorNode = node.get(discriminatorPropertyName);
74-
final String discriminatorPropertyValue = discriminatorNode == null
75-
? null
76-
: discriminatorNode.textValue();
77-
78-
final JsonSchema jsonSchema = parentSchema;
79-
checkDiscriminatorMatch(
80-
currentDiscriminatorContext,
81-
discriminator,
82-
discriminatorPropertyValue,
83-
jsonSchema);
62+
try {
63+
errors.addAll(schema.validate(node, rootNode, at));
64+
65+
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
66+
final Iterator<JsonNode> arrayElements = schemaNode.elements();
67+
while (arrayElements.hasNext()) {
68+
final ObjectNode allOfEntry = (ObjectNode) arrayElements.next();
69+
final JsonNode $ref = allOfEntry.get("$ref");
70+
if (null != $ref) {
71+
final ValidationContext.DiscriminatorContext currentDiscriminatorContext = validationContext
72+
.getCurrentDiscriminatorContext();
73+
if (null != currentDiscriminatorContext) {
74+
final ObjectNode discriminator = currentDiscriminatorContext
75+
.getDiscriminatorForPath(allOfEntry.get("$ref").asText());
76+
if (null != discriminator) {
77+
registerAndMergeDiscriminator(currentDiscriminatorContext, discriminator, parentSchema, at);
78+
// now we have to check whether we have hit the right target
79+
final String discriminatorPropertyName = discriminator.get("propertyName").asText();
80+
final JsonNode discriminatorNode = node.get(discriminatorPropertyName);
81+
final String discriminatorPropertyValue = discriminatorNode == null
82+
? null
83+
: discriminatorNode.textValue();
84+
85+
final JsonSchema jsonSchema = parentSchema;
86+
checkDiscriminatorMatch(
87+
currentDiscriminatorContext,
88+
discriminator,
89+
discriminatorPropertyValue,
90+
jsonSchema);
91+
}
8492
}
8593
}
8694
}
8795
}
96+
} finally {
97+
if (errors.isEmpty()) {
98+
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
99+
backupEvaluatedPropertiesList.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
100+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
101+
} else {
102+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
103+
}
88104
}
89105
}
90106

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

+25
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
6161
Set<ValidationMessage> allErrors = new LinkedHashSet<ValidationMessage>();
6262
String typeValidatorName = "anyOf/type";
6363

64+
// As anyOf might contain multiple schemas take a backup of evaluatedProperties.
65+
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
66+
67+
// Make the evaluatedProperties list empty.
68+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
69+
6470
try {
6571
for (JsonSchema schema : schemas) {
6672
if (schema.getValidators().containsKey(typeValidatorName)) {
@@ -74,11 +80,18 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7480
}
7581
Set<ValidationMessage> errors = schema.validate(node, rootNode, at);
7682
if (errors.isEmpty() && (!this.validationContext.getConfig().isOpenAPI3StyleDiscriminators())) {
83+
// Clear all errors.
84+
allErrors.clear();
85+
// return empty errors.
7786
return errors;
7887
} else if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
7988
if (discriminatorContext.isDiscriminatorMatchFound()) {
8089
if (!errors.isEmpty()) {
8190
errors.add(buildValidationMessage(at, DISCRIMINATOR_REMARK));
91+
allErrors.addAll(errors);
92+
} else {
93+
// Clear all errors.
94+
allErrors.clear();
8295
}
8396
return errors;
8497
}
@@ -95,10 +108,22 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
95108
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
96109
validationContext.leaveDiscriminatorContextImmediately(at);
97110
}
111+
if (allErrors.isEmpty()) {
112+
addEvaluatedProperties(backupEvaluatedProperties);
113+
} else {
114+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
115+
}
98116
}
99117
return Collections.unmodifiableSet(allErrors);
100118
}
101119

120+
private void addEvaluatedProperties(Object backupEvaluatedProperties) {
121+
// Add all the evaluated properties.
122+
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
123+
backupEvaluatedPropertiesList.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
124+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
125+
}
126+
102127
@Override
103128
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
104129
ArrayList<Set<ValidationMessage>> results = new ArrayList<>(schemas.size());

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

+62-10
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,73 @@ public IfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSche
5757
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
5858
debug(logger, node, rootNode, at);
5959

60+
// As if-then-else might contain multiple schemas take a backup of evaluatedProperties.
61+
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
62+
63+
Object ifEvaluatedProperties = null;
64+
65+
Object thenEvaluatedProperties = null;
66+
67+
Object elseEvaluatedProperties = null;
68+
69+
// Make the evaluatedProperties list empty.
70+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
71+
6072
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
6173

6274
boolean ifConditionPassed;
6375
try {
64-
ifConditionPassed = ifSchema.validate(node, rootNode, at).isEmpty();
65-
} catch (JsonSchemaException ex) {
66-
// When failFast is enabled, validations are thrown as exceptions.
67-
// An exception means the condition failed
68-
ifConditionPassed = false;
69-
}
76+
try {
77+
ifConditionPassed = ifSchema.validate(node, rootNode, at).isEmpty();
78+
} catch (JsonSchemaException ex) {
79+
// When failFast is enabled, validations are thrown as exceptions.
80+
// An exception means the condition failed
81+
ifConditionPassed = false;
82+
}
83+
// Evaluated Properties from if.
84+
ifEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
85+
86+
if (ifConditionPassed && thenSchema != null) {
87+
88+
// Make the evaluatedProperties list empty.
89+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
90+
91+
errors.addAll(thenSchema.validate(node, rootNode, at));
92+
93+
// Collect the then evaluated properties.
94+
thenEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
7095

71-
if (ifConditionPassed && thenSchema != null) {
72-
errors.addAll(thenSchema.validate(node, rootNode, at));
73-
} else if (!ifConditionPassed && elseSchema != null) {
74-
errors.addAll(elseSchema.validate(node, rootNode, at));
96+
} else if (!ifConditionPassed && elseSchema != null) {
97+
98+
// Make the evaluatedProperties list empty.
99+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
100+
101+
errors.addAll(elseSchema.validate(node, rootNode, at));
102+
103+
// Collect the else evaluated properties.
104+
elseEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
105+
}
106+
107+
} finally {
108+
if (errors.isEmpty()) {
109+
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
110+
111+
if (ifEvaluatedProperties != null) {
112+
backupEvaluatedPropertiesList.addAll((List<String>) ifEvaluatedProperties);
113+
}
114+
115+
if (thenEvaluatedProperties != null) {
116+
backupEvaluatedPropertiesList.addAll((List<String>) thenEvaluatedProperties);
117+
}
118+
119+
if (elseEvaluatedProperties != null) {
120+
backupEvaluatedPropertiesList.addAll((List<String>) elseEvaluatedProperties);
121+
}
122+
123+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
124+
} else {
125+
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
126+
}
75127
}
76128

77129
return Collections.unmodifiableSet(errors);

0 commit comments

Comments
 (0)