Skip to content

[WIP] Implement initialisation pass of the schema to find all $id references #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7f5f602
We will need a schema cache in each schema to handle their `$id` entr…
KayEss Jul 3, 2019
f0fb3f9
clang-format
KayEss Jul 3, 2019
5f75ce4
It's very important that this does not end up as an alias for `nullptr`.
KayEss Jul 3, 2019
03d1f52
Add an initial pass when a schema is first loaded to look for embedde…
KayEss Jul 4, 2019
74693f2
Change the cache map to be keyed on actual URLs
KayEss Jul 4, 2019
8f3fc4b
Preloading requires knowledge of the base URL so we can calculate the…
KayEss Jul 4, 2019
9fdda79
Deal better with URL fragments that aren't JSON pointers.
KayEss Jul 4, 2019
926bfd4
clang-format
KayEss Jul 4, 2019
1c64ba5
Temporarily lock to and older version of the test suite.
KayEss Jul 4, 2019
fae7cb7
Merge branch 'temp-pin' into feature/two-pass
KayEss Jul 4, 2019
6ee7fd5
Propogate URL type up call chain.
KayEss Jul 8, 2019
0ec72a6
We must rely on the base URL set for the schema in its constructor
KayEss Jul 8, 2019
15dde4f
Normalise all URLs in schema caches to always have a fragment.
KayEss Jul 9, 2019
4234665
Deprecate cache lookups by `f5::u8view`
KayEss Jul 9, 2019
d1d8e89
Always strip the fragment when loading a schema.
KayEss Jul 9, 2019
e5ea8ac
clang-format
KayEss Jul 9, 2019
d826c9a
Properly preserve the original URL.
KayEss Jul 10, 2019
e68ac41
Refactor the cache lookup to return the sub-schema part for json poin…
KayEss Jul 16, 2019
d02cb29
Remove some memory allocations in regex pattern matching.
KayEss Jul 18, 2019
e44618c
Make all URLs relative to the same base.
KayEss Jul 21, 2019
567db59
Print some timing information with verbose output.
KayEss Jul 22, 2019
f153981
Fix up the build system to parallel run the test suite tests.
KayEss Jul 22, 2019
e372d86
Add in the new behaviour that we expect to be able to use.
KayEss Jul 22, 2019
a892962
We need to remove the schema cache from the annotation code.
KayEss Jul 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
2019-07-03 Kirit Saelensminde <[email protected]>
Add an initial pass when a schema is first loaded to look for embedded `$id`s so they can be used from anywhere else in the schema.

2019-01-22 Kirit Saelensminde <[email protected]>
Add debug logging for the HTTP schema loader.

Expand Down
16 changes: 12 additions & 4 deletions include/f5/json/schema.cache.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
Copyright 2018, Proteus Technologies Co Ltd. <https://support.felspar.com/>
Copyright 2018-2019, Proteus Technologies Co Ltd.
<https://support.felspar.com/>

Distributed under the Boost Software License, Version 1.0.
See <http://www.boost.org/LICENSE_1_0.txt>
Expand All @@ -24,7 +25,7 @@ namespace f5 {
friend validation::annotations;

std::shared_ptr<schema_cache> base;
std::map<fostlib::string, schema> cache;
std::map<fostlib::url, schema> cache;

public:
/// Create an empty cache which uses the root cache
Expand All @@ -34,17 +35,24 @@ namespace f5 {
schema_cache(std::shared_ptr<schema_cache>);

/// Perform a lookup in this case and its bases
const schema &operator[](f5::u8view) const;
std::pair<schema const &, fostlib::jcursor>
operator[](fostlib::url) const;
[[deprecated("Only call with a fostlib::url")]] schema const &
operator[](f5::u8view) const;

/// The root cache. The root cache is the only cache which
/// should have an empty base.
static std::shared_ptr<schema_cache> root_cache();

/// Add a schema at a given position in the cache
const schema &insert(fostlib::string, schema);
const schema &insert(fostlib::url, schema);
/// Add a schema at an unnamed position, i.e. only if it
/// contains a `$id` describing its proper location
const schema &insert(schema);

private:
/// Looks for an exact match
schema const *recursive_lookup(fostlib::url const &) const;
};


Expand Down
6 changes: 6 additions & 0 deletions include/f5/json/schema.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace f5 {
namespace json {


class schema_cache;


/**
## JSON Schema

Expand All @@ -31,6 +34,9 @@ namespace f5 {
public:
schema(const fostlib::url &, value v);

/// We need to pre-load this with $id schemas that we find
std::shared_ptr<schema_cache> schemas;

const fostlib::url &self() const { return id; }
value assertions() const { return validation; }

Expand Down
5 changes: 3 additions & 2 deletions include/f5/json/validator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ namespace f5 {
value data;
pointer dpos;

std::shared_ptr<schema_cache> schemas;

private:
friend class json::schema;
/// Construct the initial location
Expand Down Expand Up @@ -78,6 +76,9 @@ namespace f5 {
/// on the local $id found in parent lexical scopes of the
/// JSON
fostlib::url spos_url() const;

/// Return the schema cache for the current schema
schema_cache const &schemas() const;
};


Expand Down
36 changes: 28 additions & 8 deletions json-schema-validator/valid.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
Copyright 2018, Proteus Technologies Co Ltd. <https://support.felspar.com/>
Copyright 2018-2019, Proteus Technologies Co Ltd.
<https://support.felspar.com/>

Distributed under the Boost Software License, Version 1.0.
See <http://www.boost.org/LICENSE_1_0.txt>
Expand All @@ -9,6 +10,7 @@

#include <fost/file>
#include <fost/main>
#include <fost/timer>
#include <fost/unicode>


Expand Down Expand Up @@ -41,8 +43,7 @@ namespace {
std::cout << "Assertion: " << e.assertion
<< "\nSchema position: " << e.spos
<< "\nData position: " << e.dpos
<< "\nSchema: " << s.assertions()[e.spos]
<< "\nData: " << d[e.dpos] << std::endl;
<< "\nData at that position: " << d[e.dpos] << std::endl;
}
}

Expand All @@ -53,23 +54,42 @@ FSL_MAIN("json-schema-validator", "JSON Schema Validator")
args.commandSwitch("v", c_verbose);
args.commandSwitch("-schema", c_schema);

const f5::json::schema s{fostlib::url{}, load_json(c_schema.value())};
fostlib::timer time;

if (c_verbose.value()) {
std::cout << 0.0 << " Loading schema JSON " << c_schema.value()
<< std::endl;
}
f5::json::value parsed = load_json(c_schema.value());
if (c_verbose.value()) {
std::cout << time.seconds() << " Establishing as schema" << std::endl;
}
f5::json::schema const s{fostlib::url{}, parsed};

for (const auto &arg : args) {
if (c_verbose.value()) {
std::cout << "Loading and validating " << arg << std::endl;
std::cout << time.seconds() << "Loading " << arg << std::endl;
}
const auto j = load_json(arg);
if (c_verbose.value()) {
std::cout << time.seconds() << " Validating " << std::endl;
}
auto v = s.validate(j);
if (c_verbose.value()) std::cout << time.seconds() << " Results in\n";
if (c_check_invalid.value()) {
if (const auto v = s.validate(j); v) {
if (v) {
std::cout << arg << " validated when it should not have"
<< std::endl;
return 2;
}
} else {
if (auto v = s.validate(j); not v) {
if (not v) {
if (c_verbose.value())
std::cout << time.seconds() << " Results in\n";
std::cout << arg << " did not validate" << std::endl;
print(s, j, (f5::json::validation::result::error)std::move(v));
print(s, j,
static_cast<f5::json::validation::result::error>(
std::move(v)));
return 1;
}
}
Expand Down
40 changes: 17 additions & 23 deletions src/annotations.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
Copyright 2018, Proteus Technologies Co Ltd. <https://support.felspar.com/>
Copyright 2018-2019, Proteus Technologies Co Ltd. <https://support.felspar.com/>

Distributed under the Boost Software License, Version 1.0.
See <http://www.boost.org/LICENSE_1_0.txt>
Expand All @@ -22,9 +22,9 @@ namespace {
std::shared_ptr<f5::json::schema_cache> schemas) {
if (anp->sroot[anp->spos].has_key("$id")) {
if (not schemas) {
schemas =
std::make_shared<f5::json::schema_cache>(anp->schemas);
anp->schemas = schemas;
// schemas =
// std::make_shared<f5::json::schema_cache>(anp->schemas);
// anp->schemas = schemas;
}
anp->base = &schemas->insert(
f5::json::schema{anp->base->self(), anp->sroot[anp->spos]});
Expand All @@ -38,11 +38,10 @@ namespace {
for (const auto &def :
anp->sroot[anp->spos][sub]["definitions"].object()) {
fostlib::url r{anp->base->self(), anp->spos / sub};
const auto &subschema = anp->schemas->insert(
fostlib::string(r.as_string()),
f5::json::schema{base, def.second});
definitions(
anp, subschema.self(), sub / "definitions" / def.first);
// const auto &subschema = anp->schemas->insert(
// r, f5::json::schema{base, def.second});
// definitions(
// anp, subschema.self(), sub / "definitions" / def.first);
}
}
}
Expand All @@ -55,10 +54,7 @@ f5::json::validation::annotations::annotations(
sroot(s.assertions()),
spos(std::move(sp)),
data(std::move(d)),
dpos(std::move(dp)),
schemas{std::make_shared<schema_cache>()} {
id_handling(this, schemas);
definitions(this, base->self(), pointer{});
dpos(std::move(dp)) {
}


Expand All @@ -68,10 +64,7 @@ f5::json::validation::annotations::annotations(
sroot(s.assertions()),
spos(std::move(sp)),
data(std::move(d)),
dpos(std::move(dp)),
schemas{std::make_shared<schema_cache>(an.schemas)} {
id_handling(this, schemas);
definitions(this, base->self(), pointer{});
dpos(std::move(dp)) {
}


Expand All @@ -81,9 +74,7 @@ f5::json::validation::annotations::annotations(
sroot(an.sroot),
spos(std::move(sp)),
data(an.data),
dpos(std::move(dp)),
schemas(an.schemas) {
id_handling(this, nullptr);
dpos(std::move(dp)) {
}


Expand All @@ -92,9 +83,7 @@ f5::json::validation::annotations::annotations(annotations &&b, result &&w)
sroot{std::move(b.sroot)},
spos{std::move(b.spos)},
data{std::move(b.data)},
dpos{std::move(b.dpos)},
schemas{b.schemas} {
id_handling(this, nullptr);
dpos{std::move(b.dpos)} {
merge(std::move(w));
}

Expand Down Expand Up @@ -127,3 +116,8 @@ fostlib::url f5::json::validation::annotations::spos_url() const {
}
return u;
}


auto f5::json::validation::annotations::schemas() const -> schema_cache const & {
return *base->schemas;
}
8 changes: 4 additions & 4 deletions src/assertions.numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ namespace {
return p(bound.value(), v)
? f5::json::validation::result{std::move(an)}
: f5::json::validation::result{
rule, an.spos, an.dpos};
rule, an.spos, an.dpos};
},
[&](double v) mutable {
return p(bound.value(), v)
? f5::json::validation::result{std::move(an)}
: f5::json::validation::result{
rule, an.spos, an.dpos};
rule, an.spos, an.dpos};
},
[&](const auto &) mutable {
return f5::json::validation::result{std::move(an)};
Expand All @@ -46,7 +46,7 @@ namespace {
return p(bound.value(), v)
? f5::json::validation::result{std::move(an)}
: f5::json::validation::result{
rule, an.spos, an.dpos};
rule, an.spos, an.dpos};
},
[&](double v) mutable {
bool passed;
Expand All @@ -58,7 +58,7 @@ namespace {
return passed
? f5::json::validation::result{std::move(an)}
: f5::json::validation::result{
rule, an.spos, an.dpos};
rule, an.spos, an.dpos};
},
[&](const auto &) mutable {
return f5::json::validation::result{std::move(an)};
Expand Down
4 changes: 2 additions & 2 deletions src/assertions.string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const f5::json::assertion::checker f5::json::assertion::pattern_checker =
auto string = fostlib::coerce<std::optional<f5::u8view>>(
an.data[an.dpos]);
if (not string) return validation::result{std::move(an)};
std::regex re{static_cast<std::string>(
fostlib::coerce<fostlib::string>(part))};
auto const rgx = fostlib::coerce<f5::u8view>(part);
std::regex re{rgx.data(), rgx.memory().size()};
if (std::regex_search(
string->data(), string->data() + string->bytes(), re)) {
return validation::result{std::move(an)};
Expand Down
Loading