Skip to content

Commit

Permalink
implements prefixItems affect items
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-toepfer committed Oct 5, 2023
1 parent 04d8199 commit a4b60d9
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@
*/
package io.github.sebastiantoepfer.jsonschema;

import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import jakarta.json.JsonValue;
import java.util.Optional;

public interface JsonSchema extends JsonValue {
public Validator validator();

Optional<Keyword> keywordByName(String name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
*/
package io.github.sebastiantoepfer.jsonschema;

import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import io.github.sebastiantoepfer.jsonschema.spi.JsonSchemaFactory;
import jakarta.json.JsonValue;
import java.util.Optional;

public final class FakeJsonSchemaFactory implements JsonSchemaFactory {

Expand All @@ -40,6 +42,11 @@ public Validator validator() {
public JsonValue.ValueType getValueType() {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public Optional<Keyword> keywordByName(String name) {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
import jakarta.json.JsonValue;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

final class DefaultJsonSchema extends AbstractJsonValueSchema {

Expand All @@ -55,16 +55,19 @@ public DefaultJsonSchema(final JsonObject value) {

@Override
public Validator validator() {
return asJsonObject()
.entrySet()
.stream()
return keywords()
.map(this::asContraint)
.flatMap(Optional::stream)
.collect(
collectingAndThen(toList(), constraints -> new DefaultValidator(new AllOfConstraint<>(constraints)))
);
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return keywords().filter(k -> k.hasName(name)).findFirst();
}

private Collection<VocabularyDefinition> vocabulary() {
return new KeywordSearch(new VocabularyKeywordType())
.searchForKeywordIn(this)
Expand All @@ -75,8 +78,11 @@ private Collection<VocabularyDefinition> vocabulary() {
.toList();
}

private Optional<Constraint<JsonValue>> asContraint(final Entry<String, JsonValue> property) {
final Keyword keyword = keywords.createKeywordFor(this, property);
private Stream<Keyword> keywords() {
return asJsonObject().entrySet().stream().map(property -> keywords.createKeywordFor(this, property));
}

private Optional<Constraint<JsonValue>> asContraint(final Keyword keyword) {
final Constraint<JsonValue> result;
if (keyword.hasCategory(Keyword.KeywordCategory.ASSERTION)) {
result = new AssertionConstraint(keyword.asAssertion());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

import io.github.sebastiantoepfer.jsonschema.Validator;
import io.github.sebastiantoepfer.jsonschema.core.constraint.NoConstraint;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import jakarta.json.JsonValue;
import java.util.Optional;

final class EmptyJsonSchema extends AbstractJsonValueSchema {

Expand All @@ -37,4 +39,9 @@ public EmptyJsonSchema() {
public Validator validator() {
return new DefaultValidator(new NoConstraint<>());
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

import io.github.sebastiantoepfer.jsonschema.Validator;
import io.github.sebastiantoepfer.jsonschema.core.constraint.UnfulfillableConstraint;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import jakarta.json.JsonValue;
import java.util.Optional;

final class FalseJsonSchema extends AbstractJsonValueSchema {

Expand All @@ -37,4 +39,9 @@ public FalseJsonSchema() {
public Validator validator() {
return new DefaultValidator(new UnfulfillableConstraint<>());
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

import io.github.sebastiantoepfer.jsonschema.Validator;
import io.github.sebastiantoepfer.jsonschema.core.constraint.NoConstraint;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import jakarta.json.JsonValue;
import java.util.Optional;

final class TrueJsonSchema extends AbstractJsonValueSchema {

Expand All @@ -37,4 +39,9 @@ public TrueJsonSchema() {
public Validator validator() {
return new DefaultValidator(new NoConstraint<>());
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public ApplicatorVocabulary() {
this.vocab =
new DefaultVocabulary(
URI.create("https://json-schema.org/draft/2020-12/vocab/applicator"),
new PrefixItemsKeywordType(),
new ItemsKeywordType()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType;
import jakarta.json.JsonArray;
import jakarta.json.JsonNumber;
import jakarta.json.JsonValue;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

final class ItemsKeywordType implements KeywordType {

Expand Down Expand Up @@ -75,6 +77,11 @@ public ValueType getValueType() {
return schema.getValueType();
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return schema.keywordByName(name);
}

@Override
public boolean hasName(final String name) {
return Objects.equals(name(), name);
Expand All @@ -87,7 +94,17 @@ public Collection<KeywordCategory> categories() {

@Override
public JsonValue value() {
return JsonValue.TRUE;
final JsonValue result;
if (appliesToAny()) {
result = JsonValue.TRUE;
} else {
result = JsonValue.FALSE;
}
return result;
}

private boolean appliesToAny() {
return startIndex() < 0;
}

@Override
Expand All @@ -97,7 +114,18 @@ public boolean applyTo(final JsonValue instance) {

private boolean matchesSchema(final JsonArray items) {
final Validator itemValidator = validator();
return items.stream().map(itemValidator::validate).allMatch(Collection::isEmpty);
return items.stream().skip(startIndex() + 1).map(itemValidator::validate).allMatch(Collection::isEmpty);
}

private long startIndex() {
return owner()
.keywordByName("prefixItems")
.map(Keyword::asAnnotation)
.map(Annotation::value)
.filter(InstanceType.INTEGER::isInstance)
.map(JsonNumber.class::cast)
.map(JsonNumber::longValue)
.orElse(-1L);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import static org.hamcrest.Matchers.is;

import io.github.sebastiantoepfer.jsonschema.Validator;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import java.util.Optional;
import org.junit.jupiter.api.Test;

class AbstractJsonValueSchemaTest {
Expand Down Expand Up @@ -65,5 +67,10 @@ public MyJsonValueSchema(JsonValue value) {
public Validator validator() {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public Optional<Keyword> keywordByName(final String name) {
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
package io.github.sebastiantoepfer.jsonschema.core;

import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty;
import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -76,4 +78,38 @@ void should_not_be_loadable_without_mandantory_core_vocabulary() {

Assertions.assertThrows(Exception.class, () -> new DefaultJsonSchema(invalidSchema));
}

@Test
void should_find_keyword_by_name() {
assertThat(
new DefaultJsonSchema(
Json
.createObjectBuilder()
.add(
"$vocabulary",
Json.createObjectBuilder().add("https://json-schema.org/draft/2020-12/vocab/core", true)
)
.build()
)
.keywordByName("$vocabulary"),
isPresent()
);
}

@Test
void should_return_empty_for_non_existing_keyword() {
assertThat(
new DefaultJsonSchema(
Json
.createObjectBuilder()
.add(
"$vocabulary",
Json.createObjectBuilder().add("https://json-schema.org/draft/2020-12/vocab/core", true)
)
.build()
)
.keywordByName("type"),
isEmpty()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
package io.github.sebastiantoepfer.jsonschema.core.vocab.applicator;

import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty;
import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -116,4 +118,79 @@ void should_produces_true_if_is_applied_to_any_instance() {
is(JsonValue.TRUE)
);
}

@Test
void should_find_know_keyword() {
assertThat(
((JsonSubSchema) new ItemsKeywordType()
.createKeyword(
new DefaultJsonSchemaFactory().create(JsonValue.TRUE),
Json.createObjectBuilder().add("type", "string").build()
)).keywordByName("type"),
isPresent()
);
}

@Test
void should_retrun_empty_for_non_existing_keyword() {
assertThat(
((JsonSubSchema) new ItemsKeywordType()
.createKeyword(
new DefaultJsonSchemaFactory().create(JsonValue.TRUE),
Json.createObjectBuilder().add("type", "string").build()
)).keywordByName("properties"),
isEmpty()
);
}

@Test
void should_return_false_if_not_applies_to_any_item() {
assertThat(
new ItemsKeywordType()
.createKeyword(
new DefaultJsonSchemaFactory()
.create(
Json.createObjectBuilder().add("prefixItems", Json.createArrayBuilder().add(true)).build()
),
JsonValue.FALSE
)
.asAnnotation()
.value(),
is(JsonValue.FALSE)
);
}

@Test
void should_be_valid_if_invaliditem_is_already_checked_by_prefixItems() {
assertThat(
new ItemsKeywordType()
.createKeyword(
new DefaultJsonSchemaFactory()
.create(
Json.createObjectBuilder().add("prefixItems", Json.createArrayBuilder().add(true)).build()
),
JsonValue.FALSE
)
.asApplicator()
.applyTo(Json.createArrayBuilder().add("1").build()),
is(true)
);
}

@Test
void should_be_invalid_if_invaliditem_is_not_already_checked_by_prefixItems() {
assertThat(
new ItemsKeywordType()
.createKeyword(
new DefaultJsonSchemaFactory()
.create(
Json.createObjectBuilder().add("prefixItems", Json.createArrayBuilder().add(true)).build()
),
JsonValue.FALSE
)
.asApplicator()
.applyTo(Json.createArrayBuilder().add("1").add("2").build()),
is(false)
);
}
}

0 comments on commit a4b60d9

Please sign in to comment.