Skip to content

Commit

Permalink
do not longer lazyload officalvocabs
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-toepfer committed Jun 9, 2024
1 parent 1563195 commit 2d53136
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,21 @@

import io.github.sebastiantoepfer.ddd.common.Media;
import io.github.sebastiantoepfer.ddd.media.json.JsonObjectPrintable;
import io.github.sebastiantoepfer.jsonschema.Vocabulary;
import io.github.sebastiantoepfer.jsonschema.core.vocab.OfficialVocabularies;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularyDefinition;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.ServiceLoaderLazyVocabulariesSupplier;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.VocabularyDefinition;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.VocabularyDefinitions;
import jakarta.json.JsonObject;
import jakarta.json.JsonValue;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
Expand All @@ -53,13 +58,18 @@ final class VocabularyKeyword implements VocabularyDefinitions {

static final String NAME = "$vocabulary";
private final JsonObject vocabularies;
private final Supplier<Stream<LazyVocabularies>> lazyVocabulariesSupplier;

VocabularyKeyword(final JsonValue vocabularies) {
this(vocabularies.asJsonObject());
VocabularyKeyword(final JsonValue vocabularies, final Supplier<Stream<LazyVocabularies>> lazyVocabulariesSupplier) {
this(vocabularies.asJsonObject(), lazyVocabulariesSupplier);
}

VocabularyKeyword(final JsonObject vocabularies) {
VocabularyKeyword(
final JsonObject vocabularies,
final Supplier<Stream<LazyVocabularies>> lazyVocabulariesSupplier
) {
this.vocabularies = Objects.requireNonNull(vocabularies);
this.lazyVocabulariesSupplier = Objects.requireNonNull(lazyVocabulariesSupplier);
}

@Override
Expand All @@ -80,16 +90,38 @@ public Collection<KeywordCategory> categories() {

@Override
public Stream<VocabularyDefinition> definitions() {
return vocabularies
.entrySet()
.stream()
.map(
entry ->
new LazyVocabularyDefinition(
URI.create(entry.getKey()),
entry.getValue() == JsonValue.TRUE,
new ServiceLoaderLazyVocabulariesSupplier()
)
);
return vocabularies.entrySet().stream().map(LazyNonOfficalVocabularyDefinition::new);
}

private final class LazyNonOfficalVocabularyDefinition implements VocabularyDefinition {

private final URI id;
private final JsonValue required;

public LazyNonOfficalVocabularyDefinition(final Map.Entry<String, JsonValue> property) {
this(URI.create(property.getKey()), property.getValue());
}

public LazyNonOfficalVocabularyDefinition(final URI id, final JsonValue required) {
this.id = Objects.requireNonNull(id);
this.required = required;
}

@Override
public Optional<Vocabulary> findVocabulary() {
return new OfficialVocabularies()
.loadVocabularyWithId(id)
.or(() -> new LazyVocabularyDefinition(id, isRequired(), lazyVocabulariesSupplier).findVocabulary());
}

@Override
public boolean hasid(final URI id) {
return Objects.equals(this.id, id);
}

@Override
public boolean isRequired() {
return JsonValue.TRUE.equals(required);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.github.sebastiantoepfer.jsonschema.InstanceType;
import io.github.sebastiantoepfer.jsonschema.JsonSchema;
import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.ServiceLoaderLazyVocabulariesSupplier;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.VocabularyDefinitions;
import jakarta.json.JsonValue;

Expand All @@ -34,6 +35,12 @@
*/
public final class VocabularyKeywordType implements KeywordType {

private final ServiceLoaderLazyVocabulariesSupplier lazyVocabulariesSupplier;

public VocabularyKeywordType() {
this.lazyVocabulariesSupplier = new ServiceLoaderLazyVocabulariesSupplier();
}

@Override
public String name() {
return VocabularyKeyword.NAME;
Expand All @@ -44,7 +51,7 @@ public VocabularyDefinitions createKeyword(final JsonSchema schema) {
final JsonValue value = schema.asJsonObject().get((name()));
final VocabularyKeyword result;
if (InstanceType.OBJECT.isInstance(value)) {
result = new VocabularyKeyword(value);
result = new VocabularyKeyword(value, lazyVocabulariesSupplier);
} else {
throw new IllegalArgumentException(
"must be an object! " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,18 @@
import static org.hamcrest.Matchers.is;

import io.github.sebastiantoepfer.ddd.media.core.HashMapMedia;
import io.github.sebastiantoepfer.jsonschema.Vocabulary;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.ListVocabulary;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.VocabularyDefinition;
import jakarta.json.Json;
import jakarta.json.JsonValue;
import java.net.URI;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
Expand All @@ -46,7 +52,7 @@ class VocabularyKeywordTest {

@Test
void should_created_keyword_should_know_his_name() {
final Keyword vocabulary = new VocabularyKeyword(JsonValue.EMPTY_JSON_OBJECT);
final Keyword vocabulary = new VocabularyKeyword(JsonValue.EMPTY_JSON_OBJECT, Stream::empty);

assertThat(vocabulary.hasName("$vocabulary"), is(true));
assertThat(vocabulary.hasName("$id"), is(false));
Expand All @@ -59,7 +65,8 @@ void should_create_definitions() {
Json.createObjectBuilder()
.add("https://json-schema.org/draft/2020-12/vocab/core", true)
.add("http://openapi.org/test", false)
.build()
.build(),
Stream::empty
)
.definitions()
.toList(),
Expand All @@ -77,7 +84,8 @@ void should_be_printable() {
Json.createObjectBuilder()
.add("https://json-schema.org/draft/2020-12/vocab/core", true)
.add("http://openapi.org/test", false)
.build()
.build(),
Stream::empty
).printOn(new HashMapMedia()),
(Matcher) hasEntry(
is("$vocabulary"),
Expand All @@ -89,6 +97,67 @@ void should_be_printable() {
);
}

@Test
void should_not_lazy_load_offical_vocabs() {
//security and peformance. it should not be possible to override keywords define by this module!
assertThat(
new VocabularyKeyword(
Json.createObjectBuilder().add("https://json-schema.org/draft/2020-12/vocab/core", true).build(),
Stream::empty
)
.definitions()
.map(VocabularyDefinition::findVocabulary)
.flatMap(Optional::stream)
.map(Vocabulary::id)
.toList(),
containsInAnyOrder(URI.create("https://json-schema.org/draft/2020-12/vocab/core"))
);
}

@Test
void should_provide_vocab_which_know_they_id() {
final VocabularyDefinition vocabDef = new VocabularyKeyword(
Json.createObjectBuilder().add("https://json-schema.org/draft/2020-12/vocab/core", true).build(),
Stream::empty
)
.definitions()
.findFirst()
.orElseThrow();

assertThat(vocabDef.hasid(URI.create("https://json-schema.org/draft/2020-12/vocab/core")), is(true));
assertThat(vocabDef.hasid(URI.create("http://openapi.org/test")), is(false));
}

@Test
void should_lazy_load_non_offical_vocabs() {
assertThat(
new VocabularyKeyword(
Json.createObjectBuilder()
.add("https://json-schema.org/draft/2020-12/vocab/core", true)
.add("http://openapi.org/test", false)
.build(),
() ->
Stream.of(
new LazyVocabularies() {
@Override
public Optional<Vocabulary> loadVocabularyWithId(final URI id) {
return Optional.of(new ListVocabulary(id, List.of()));
}
}
)
)
.definitions()
.map(VocabularyDefinition::findVocabulary)
.flatMap(Optional::stream)
.map(Vocabulary::id)
.toList(),
containsInAnyOrder(
URI.create("https://json-schema.org/draft/2020-12/vocab/core"),
URI.create("http://openapi.org/test")
)
);
}

private static class VocabularyDefinitionMatcher extends TypeSafeMatcher<VocabularyDefinition> {

private final URI id;
Expand Down

0 comments on commit 2d53136

Please sign in to comment.