Skip to content

Commit

Permalink
Ensures context is reset after validating regardless of which method …
Browse files Browse the repository at this point in the history
…is used by the client. (#812)

Resolves #810

Co-authored-by: Faron Dutton <faron.dutton@insightglobal.com>
  • Loading branch information
fdutton and Faron Dutton committed Jun 7, 2023
1 parent efbb37e commit 9d78bf3
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 77 deletions.
19 changes: 17 additions & 2 deletions src/main/java/com/networknt/schema/CollectorContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public CollectorContext() {
public CollectorContext(boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
this.disableUnevaluatedItems = disableUnevaluatedItems;
this.disableUnevaluatedProperties = disableUnevaluatedProperties;
this.dynamicScopes.push(newScope());
this.dynamicScopes.push(newTopScope());
}

/**
Expand Down Expand Up @@ -187,7 +187,7 @@ public void reset() {
this.collectorMap = new HashMap<>();
this.collectorLoadMap = new HashMap<>();
this.dynamicScopes.clear();
this.dynamicScopes.push(newScope());
this.dynamicScopes.push(newTopScope());
}

/**
Expand All @@ -208,6 +208,10 @@ private Scope newScope() {
return new Scope(this.disableUnevaluatedItems, this.disableUnevaluatedProperties);
}

private Scope newTopScope() {
return new Scope(true, this.disableUnevaluatedItems, this.disableUnevaluatedProperties);
}

public static class Scope {

/**
Expand All @@ -220,7 +224,14 @@ public static class Scope {
*/
private final Collection<String> evaluatedProperties;

private final boolean top;

Scope(boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
this(false, disableUnevaluatedItems, disableUnevaluatedProperties);
}

Scope(boolean top, boolean disableUnevaluatedItems, boolean disableUnevaluatedProperties) {
this.top = top;
this.evaluatedItems = newCollection(disableUnevaluatedItems);
this.evaluatedProperties = newCollection(disableUnevaluatedProperties);
}
Expand Down Expand Up @@ -251,6 +262,10 @@ public int size() {
};
}

public boolean isTop() {
return this.top;
}

/**
* Identifies which array items have been evaluated.
*
Expand Down
144 changes: 69 additions & 75 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ private long activeDialect() {
};

private String getCustomMessage(JsonNode schemaNode, String pname) {
if (this.validationContext.getConfig() != null && !this.validationContext.getConfig().isCustomMessageSupported()) {
if (!this.validationContext.getConfig().isCustomMessageSupported()) {
return null;
}
final JsonSchema parentSchema = getParentSchema();
Expand Down Expand Up @@ -346,18 +346,6 @@ private JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema, St

/************************ START OF VALIDATE METHODS **********************************/

@Override
public Set<ValidationMessage> validate(JsonNode node) {
try {
Set<ValidationMessage> errors = validate(node, node, atRoot());
return errors;
} finally {
if (this.validationContext.getConfig().isResetCollectorContext()) {
CollectorContext.getInstance().reset();
}
}
}

@Override
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
SchemaValidatorsConfig config = this.validationContext.getConfig();
Expand All @@ -366,56 +354,68 @@ public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, Str
CollectorContext collectorContext = getCollectorContext();
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
setValidatorState(false, true);
for (JsonValidator v : getValidators().values()) {
Set<ValidationMessage> results = Collections.emptySet();

Scope parentScope = collectorContext.enterDynamicScope();
try {
results = v.validate(jsonNode, rootNode, at);
} finally {
Scope scope = collectorContext.exitDynamicScope();
if (results.isEmpty()) {
parentScope.mergeWith(scope);
} else {
errors.addAll(results);
if (v instanceof PrefixItemsValidator || v instanceof ItemsValidator || v instanceof ItemsValidator202012 || v instanceof ContainsValidator) {
collectorContext.getEvaluatedItems().addAll(scope.getEvaluatedItems());
}
if (v instanceof PropertiesValidator || v instanceof AdditionalPropertiesValidator || v instanceof PatternPropertiesValidator) {
collectorContext.getEvaluatedProperties().addAll(scope.getEvaluatedProperties());
try {
for (JsonValidator v : getValidators().values()) {
Set<ValidationMessage> results = Collections.emptySet();

Scope parentScope = collectorContext.enterDynamicScope();
try {
results = v.validate(jsonNode, rootNode, at);
} finally {
Scope scope = collectorContext.exitDynamicScope();
if (results.isEmpty()) {
parentScope.mergeWith(scope);
} else {
errors.addAll(results);
if (v instanceof PrefixItemsValidator || v instanceof ItemsValidator
|| v instanceof ItemsValidator202012 || v instanceof ContainsValidator) {
collectorContext.getEvaluatedItems().addAll(scope.getEvaluatedItems());
}
if (v instanceof PropertiesValidator || v instanceof AdditionalPropertiesValidator
|| v instanceof PatternPropertiesValidator) {
collectorContext.getEvaluatedProperties().addAll(scope.getEvaluatedProperties());
}
}

}
}
}

if (null != config && config.isOpenAPI3StyleDiscriminators()) {
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
if (null != discriminator) {
final DiscriminatorContext discriminatorContext = this.validationContext.getCurrentDiscriminatorContext();
if (null != discriminatorContext) {
final ObjectNode discriminatorToUse;
final ObjectNode discriminatorFromContext = discriminatorContext.getDiscriminatorForPath(this.schemaPath);
if (null == discriminatorFromContext) {
// register the current discriminator. This can only happen when the current context discriminator
// was not registered via allOf. In that case we have a $ref to the schema with discriminator that gets
// used for validation before allOf validation has kicked in
discriminatorContext.registerDiscriminator(this.schemaPath, discriminator);
discriminatorToUse = discriminator;
} else {
discriminatorToUse = discriminatorFromContext;
if (config.isOpenAPI3StyleDiscriminators()) {
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
if (null != discriminator) {
final DiscriminatorContext discriminatorContext = this.validationContext
.getCurrentDiscriminatorContext();
if (null != discriminatorContext) {
final ObjectNode discriminatorToUse;
final ObjectNode discriminatorFromContext = discriminatorContext
.getDiscriminatorForPath(this.schemaPath);
if (null == discriminatorFromContext) {
// register the current discriminator. This can only happen when the current context discriminator
// was not registered via allOf. In that case we have a $ref to the schema with discriminator that gets
// used for validation before allOf validation has kicked in
discriminatorContext.registerDiscriminator(this.schemaPath, discriminator);
discriminatorToUse = discriminator;
} else {
discriminatorToUse = discriminatorFromContext;
}

final String discriminatorPropertyName = discriminatorToUse.get("propertyName").asText();
final JsonNode discriminatorNode = jsonNode.get(discriminatorPropertyName);
final String discriminatorPropertyValue = discriminatorNode == null ? null
: discriminatorNode.asText();
checkDiscriminatorMatch(discriminatorContext, discriminatorToUse, discriminatorPropertyValue,
this);
}

final String discriminatorPropertyName = discriminatorToUse.get("propertyName").asText();
final JsonNode discriminatorNode = jsonNode.get(discriminatorPropertyName);
final String discriminatorPropertyValue = discriminatorNode == null ? null : discriminatorNode.asText();
checkDiscriminatorMatch(discriminatorContext,
discriminatorToUse,
discriminatorPropertyValue,
this);
}
}

return errors;
} finally {
if (collectorContext.getDynamicScope().isTop() && config.isResetCollectorContext()) {
collectorContext.reset();
}
}
return errors;
}

public ValidationResult validateAndCollect(JsonNode node) {
Expand All @@ -433,28 +433,22 @@ public ValidationResult validateAndCollect(JsonNode node) {
* @return ValidationResult
*/
private ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNode, String at) {
try {
// Get the config.
SchemaValidatorsConfig config = this.validationContext.getConfig();
// Get the collector context from the thread local.
CollectorContext collectorContext = getCollectorContext();
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
setValidatorState(false, true);
// Validate.
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
// When walk is called in series of nested call we don't want to load the collectors every time. Leave to the API to decide when to call collectors.
if (config.doLoadCollectors()) {
// Load all the data from collectors into the context.
collectorContext.loadCollectors();
}
// Collect errors and collector context into validation result.
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
return validationResult;
} finally {
if (this.validationContext.getConfig().isResetCollectorContext()) {
CollectorContext.getInstance().reset();
}
// Get the config.
SchemaValidatorsConfig config = this.validationContext.getConfig();
// Get the collector context from the thread local.
CollectorContext collectorContext = getCollectorContext();
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
setValidatorState(false, true);
// Validate.
Set<ValidationMessage> errors = validate(jsonNode, rootNode, at);
// When walk is called in series of nested call we don't want to load the collectors every time. Leave to the API to decide when to call collectors.
if (config.doLoadCollectors()) {
// Load all the data from collectors into the context.
collectorContext.loadCollectors();
}
// Collect errors and collector context into validation result.
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
return validationResult;
}

/************************ END OF VALIDATE METHODS **********************************/
Expand Down

0 comments on commit 9d78bf3

Please sign in to comment.