Skip to content

Commit

Permalink
Returning error messages from applicators
Browse files Browse the repository at this point in the history
  • Loading branch information
EmilsOzolins committed Sep 6, 2023
1 parent 269f49a commit 5de8a5d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 29 deletions.
6 changes: 1 addition & 5 deletions src/main/java/dev/harrel/jsonschema/Applicator.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,10 @@
interface Applicator extends Evaluator {

@Override
default Result evaluate(EvaluationContext ctx, JsonNode node) {
return apply(ctx, node) ? Result.success() : Result.failure();
}
Result evaluate(EvaluationContext ctx, JsonNode node);

@Override
default Set<String> getVocabularies() {
return APPLICATOR_VOCABULARY;
}

boolean apply(EvaluationContext ctx, JsonNode node);
}
69 changes: 45 additions & 24 deletions src/main/java/dev/harrel/jsonschema/Applicators.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ public Result evaluate(EvaluationContext ctx, JsonNode node) {
.count() == filtered.size();
return valid ? Result.success(unmodifiableSet(filtered.keySet())) : Result.failure();
}

@Override
public int getOrder() {
return 10;
Expand Down Expand Up @@ -248,20 +249,22 @@ class DependentSchemasEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
public Result evaluate(EvaluationContext ctx, JsonNode node) {
if (!node.isObject()) {
return true;
return Result.success();
}


List<String> fields = node.asObject().keySet()
.stream()
.filter(dependentSchemas::containsKey)
.collect(Collectors.toList());
return fields.stream()

boolean result = fields.stream()
.map(dependentSchemas::get)
.filter(ref -> ctx.resolveInternalRefAndValidate(ref, node))
.count() == fields.size();

return result ? Result.success() : Result.failure();
}
}

Expand All @@ -276,15 +279,17 @@ class PropertyNamesEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
public Result evaluate(EvaluationContext ctx, JsonNode node) {
if (!node.isObject()) {
return true;
return Result.success();
}

Map<String, JsonNode> object = node.asObject();
return object.keySet().stream()
boolean valid = object.keySet().stream()
.filter(propName -> ctx.resolveInternalRefAndValidate(schemaRef, new StringNode(propName, node.getJsonPointer())))
.count() == object.size();

return valid ? Result.success() : Result.failure();
}
}

Expand All @@ -305,15 +310,19 @@ class IfThenElseEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
public Result evaluate(EvaluationContext ctx, JsonNode node) {
if (ctx.resolveInternalRefAndValidate(ifRef, node)) {
return thenRef
boolean valid = thenRef
.map(ref -> ctx.resolveInternalRefAndValidate(ref, node))
.orElse(true);

return valid ? Result.success() : Result.failure();
} else {
return elseRef
boolean valid = elseRef
.map(ref -> ctx.resolveInternalRefAndValidate(ref, node))
.orElse(true);

return valid ? Result.success() : Result.failure();
}
}
}
Expand All @@ -329,10 +338,12 @@ class AllOfEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
return refs.stream()
public Result evaluate(EvaluationContext ctx, JsonNode node) {
boolean valid = refs.stream()
.filter(pointer -> ctx.resolveInternalRefAndValidate(pointer, node))
.count() == refs.size();

return valid ? Result.success() : Result.failure();
}
}

Expand All @@ -347,10 +358,12 @@ class AnyOfEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
return refs.stream()
public Result evaluate(EvaluationContext ctx, JsonNode node) {
boolean valid = refs.stream()
.filter(pointer -> ctx.resolveInternalRefAndValidate(pointer, node))
.count() > 0;

return valid ? Result.success() : Result.failure();
}
}

Expand All @@ -365,10 +378,12 @@ class OneOfEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
return refs.stream()
public Result evaluate(EvaluationContext ctx, JsonNode node) {
boolean valid = refs.stream()
.filter(uri -> ctx.resolveInternalRefAndValidate(uri, node))
.count() == 1;

return valid ? Result.success() : Result.failure("Must be only valid against one of the subschemas");
}
}

Expand All @@ -384,8 +399,9 @@ class NotEvaluator implements Applicator {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
return !ctx.resolveInternalRefAndValidate(schemaUri, node);
public Result evaluate(EvaluationContext ctx, JsonNode node) {
boolean valid = !ctx.resolveInternalRefAndValidate(schemaUri, node);
return valid ? Result.success() : Result.failure();
}
}

Expand All @@ -408,9 +424,9 @@ public Set<String> getVocabularies() {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
public Result evaluate(EvaluationContext ctx, JsonNode node) {
if (!node.isArray()) {
return true;
return Result.success();
}

List<EvaluationItem> evaluationItems = unmodifiableList(ctx.getAnnotations().stream()
Expand All @@ -420,9 +436,12 @@ public boolean apply(EvaluationContext ctx, JsonNode node) {
.stream()
.filter(arrayNode -> evaluationItems.stream().noneMatch(a -> a.getInstanceLocation().startsWith(arrayNode.getJsonPointer())))
.collect(Collectors.toList());
return array.stream()

boolean valid = array.stream()
.filter(arrayNode -> ctx.resolveInternalRefAndValidate(schemaRef, arrayNode))
.count() == array.size();

return valid ? Result.success() : Result.failure();
}

@Override
Expand Down Expand Up @@ -454,9 +473,9 @@ public Set<String> getVocabularies() {
}

@Override
public boolean apply(EvaluationContext ctx, JsonNode node) {
public Result evaluate(EvaluationContext ctx, JsonNode node) {
if (!node.isObject()) {
return true;
return Result.success();
}

List<EvaluationItem> evaluationItems = unmodifiableList(ctx.getAnnotations().stream()
Expand All @@ -467,9 +486,11 @@ public boolean apply(EvaluationContext ctx, JsonNode node) {
.stream()
.filter(propertyNode -> evaluationItems.stream().noneMatch(a -> a.getInstanceLocation().startsWith(propertyNode.getJsonPointer())))
.collect(Collectors.toList());
return array.stream()

boolean valid = array.stream()
.filter(propertyNode -> ctx.resolveInternalRefAndValidate(schemaRef, propertyNode))
.count() == array.size();
return valid ? Result.success() : Result.failure();
}

@Override
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/dev/harrel/jsonschema/ValidatorResultTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,31 @@
import static org.assertj.core.api.Assertions.assertThat;

class ValidatorResultTest {

@Test
void returnsErrorMessageWhenIncorrect() {
String schema = """
{
"oneOf": [true, true]
}
""";

Validator.Result result = new ValidatorFactory().validate(schema, "null");

assertThat(result.isValid()).isFalse();
assertThat(result.getAnnotations()).isEmpty();
List<Error> errors = result.getErrors();
assertThat(errors).hasSize(1);
assertError(
errors.get(0),
"/oneOf",
"https://harrel.dev/",
"",
"oneOf",
"Must be only valid against one of the subschemas"
);
}

@Test
void returnsOnlyDirectErrors() {
String schema = """
Expand Down

0 comments on commit 5de8a5d

Please sign in to comment.