Skip to content

Commit

Permalink
Make use of JSON Schema validation from the new Blaze project (#178)
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti authored Oct 16, 2024
1 parent 57559ab commit 0b140c8
Show file tree
Hide file tree
Showing 412 changed files with 22,701 additions and 15,694 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
CXX: ${{ matrix.platform.cxx }}
steps:
- name: Install ClangFormat
run: pip install clang-format==19.1.0
run: pipx install clang-format==19.1.0

- uses: actions/checkout@v4
- name: Install dependencies (macOS)
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ option(JSONSCHEMA_CONTINUOUS "Perform a continuous JSON Schema CLI release" ON)

find_package(JSONToolkit REQUIRED)
find_package(AlterSchema REQUIRED)
find_package(Blaze REQUIRED)
find_package(JSONBinPack REQUIRED)
find_package(Hydra REQUIRED)
add_subdirectory(src)
Expand Down
11 changes: 6 additions & 5 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
vendorpull https://github.com/sourcemeta/vendorpull dea311b5bfb53b6926a4140267959ae334d3ecf4
noa https://github.com/sourcemeta/noa 517e88aef5981b88ac6bb8caff15d17dffcb4320
jsontoolkit https://github.com/sourcemeta/jsontoolkit 9abbaee71e9e00e95632858d29c7ebe5c2a723b0
hydra https://github.com/sourcemeta/hydra 3c53d3fdef79e9ba603d48470a508cc45472a0dc
alterschema https://github.com/sourcemeta/alterschema 358df64771979da64e043a416cf340d83a5382ca
jsonbinpack https://github.com/sourcemeta/jsonbinpack b25d54363f5a88cd23c1af41e4c6025b9c94d0d6
noa https://github.com/sourcemeta/noa 837e1ff981f8df45d9e2977a50f5da61d8affed4
jsontoolkit https://github.com/sourcemeta/jsontoolkit 9685d29e2e633d71319c64b1ab2fbceab865dbf3
hydra https://github.com/sourcemeta/hydra c0d2f53dc52d8febd3092ce873847729b9f447fb
alterschema https://github.com/sourcemeta/alterschema 36dc1933bbbdbf1f2574c309e41f510f58874838
jsonbinpack https://github.com/sourcemeta/jsonbinpack b09b7948f90a9e9c30a2c38441f44d1c4b93b45a
blaze https://github.com/sourcemeta/blaze 43cac42dc486be24addbc2cd0aa646d18e2000f5
5 changes: 5 additions & 0 deletions cmake/FindBlaze.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if(NOT Blaze_FOUND)
set(BLAZE_INSTALL OFF CACHE BOOL "disable installation")
add_subdirectory("${PROJECT_SOURCE_DIR}/vendor/blaze")
set(Blaze_FOUND ON)
endif()
7 changes: 7 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ target_link_libraries(jsonschema_cli PRIVATE sourcemeta::alterschema::linter)
target_link_libraries(jsonschema_cli PRIVATE sourcemeta::hydra::httpclient)
target_link_libraries(jsonschema_cli PRIVATE sourcemeta::jsonbinpack::compiler)
target_link_libraries(jsonschema_cli PRIVATE sourcemeta::jsonbinpack::runtime)
target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::compiler)
target_link_libraries(jsonschema_cli PRIVATE sourcemeta::blaze::evaluator)

# TODO: This is hack to disambiguate between JSON BinPack's "compiler" module
# and Blaze's "compiler" module, where both will try to include "compiler_export.h"
# We should fix these cases properly in Noa.
target_compile_definitions(jsonschema_cli PRIVATE SOURCEMETA_BLAZE_COMPILER_EXPORT=)

configure_file(configure.h.in configure.h @ONLY)
target_include_directories(jsonschema_cli PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
Expand Down
14 changes: 8 additions & 6 deletions src/command_compile.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <sourcemeta/jsontoolkit/json.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/blaze/evaluator.h>

#include <cstdlib> // EXIT_SUCCESS
#include <iostream> // std::cout, std::endl

Expand All @@ -20,16 +23,15 @@ auto sourcemeta::jsonschema::cli::compile(

const auto schema{sourcemeta::jsontoolkit::from_file(options.at("").front())};

const auto compiled_schema{sourcemeta::jsontoolkit::compile(
const auto compiled_schema{sourcemeta::blaze::compile(
schema, sourcemeta::jsontoolkit::default_schema_walker,
resolver(options, options.contains("h") || options.contains("http")),
sourcemeta::jsontoolkit::default_schema_compiler)};
sourcemeta::blaze::default_schema_compiler)};

const sourcemeta::jsontoolkit::JSON result{
sourcemeta::jsontoolkit::to_json(compiled_schema)};
sourcemeta::jsontoolkit::prettify(
result, std::cout,
sourcemeta::jsontoolkit::compiler_template_format_compare);
sourcemeta::blaze::to_json(compiled_schema)};
sourcemeta::jsontoolkit::prettify(result, std::cout,
sourcemeta::blaze::template_format_compare);
std::cout << std::endl;
return EXIT_SUCCESS;
}
3 changes: 1 addition & 2 deletions src/command_frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ auto sourcemeta::jsonschema::cli::frame(
sourcemeta::jsontoolkit::ReferenceMap references;
sourcemeta::jsontoolkit::frame(schema, frame, references,
sourcemeta::jsontoolkit::default_schema_walker,
resolver(options))
.wait();
resolver(options));

const auto output_json = options.contains("json") || options.contains("j");
if (output_json) {
Expand Down
11 changes: 4 additions & 7 deletions src/command_identify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,8 @@ auto sourcemeta::jsonschema::cli::identify(

// Just to print a nice warning
try {
const auto base_dialect{
sourcemeta::jsontoolkit::base_dialect(
schema, sourcemeta::jsontoolkit::official_resolver)
.get()};
const auto base_dialect{sourcemeta::jsontoolkit::base_dialect(
schema, sourcemeta::jsontoolkit::official_resolver)};
if (!base_dialect.has_value()) {
std::cerr << "warning: Cannot determine the base dialect of the schema, "
"but will attempt to guess\n";
Expand All @@ -52,9 +50,8 @@ auto sourcemeta::jsonschema::cli::identify(

try {
identifier = sourcemeta::jsontoolkit::identify(
schema, sourcemeta::jsontoolkit::official_resolver,
sourcemeta::jsontoolkit::IdentificationStrategy::Loose)
.get();
schema, sourcemeta::jsontoolkit::official_resolver,
sourcemeta::jsontoolkit::IdentificationStrategy::Loose);
} catch (const sourcemeta::jsontoolkit::SchemaError &error) {
std::cerr
<< "error: " << error.what() << "\n "
Expand Down
17 changes: 9 additions & 8 deletions src/command_metaschema.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <sourcemeta/jsontoolkit/json.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/blaze/evaluator.h>

#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cerr
#include <map> // std::map
Expand All @@ -18,7 +21,7 @@ auto sourcemeta::jsonschema::cli::metaschema(
resolver(options, options.contains("h") || options.contains("http"))};
bool result{true};

std::map<std::string, sourcemeta::jsontoolkit::SchemaCompilerTemplate> cache;
std::map<std::string, sourcemeta::blaze::Template> cache;

for (const auto &entry : for_each_json(options.at(""), parse_ignore(options),
parse_extensions(options))) {
Expand All @@ -35,17 +38,15 @@ auto sourcemeta::jsonschema::cli::metaschema(
const auto metaschema{
sourcemeta::jsontoolkit::metaschema(entry.second, custom_resolver)};
if (!cache.contains(dialect.value())) {
const auto metaschema_template{sourcemeta::jsontoolkit::compile(
const auto metaschema_template{sourcemeta::blaze::compile(
metaschema, sourcemeta::jsontoolkit::default_schema_walker,
custom_resolver, sourcemeta::jsontoolkit::default_schema_compiler)};
custom_resolver, sourcemeta::blaze::default_schema_compiler)};
cache.insert({dialect.value(), metaschema_template});
}

sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput output{metaschema};
if (sourcemeta::jsontoolkit::evaluate(
cache.at(dialect.value()), entry.second,
sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode::Fast,
std::ref(output))) {
sourcemeta::blaze::ErrorTraceOutput output{metaschema};
if (sourcemeta::blaze::evaluate(cache.at(dialect.value()), entry.second,
std::ref(output))) {
log_verbose(options)
<< "ok: " << std::filesystem::weakly_canonical(entry.first).string()
<< "\n matches " << dialect.value() << "\n";
Expand Down
18 changes: 10 additions & 8 deletions src/command_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include <sourcemeta/jsontoolkit/jsonschema.h>
#include <sourcemeta/jsontoolkit/uri.h>

#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/blaze/evaluator.h>

#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <filesystem> // std::filesystem
#include <iostream> // std::cerr, std::cout
Expand All @@ -13,7 +16,7 @@ static auto
get_schema_object(const sourcemeta::jsontoolkit::URI &identifier,
const sourcemeta::jsontoolkit::SchemaResolver &resolver)
-> std::optional<sourcemeta::jsontoolkit::JSON> {
const auto schema{resolver(identifier.recompose()).get()};
const auto schema{resolver(identifier.recompose())};
if (schema.has_value()) {
return schema;
}
Expand Down Expand Up @@ -137,12 +140,12 @@ auto sourcemeta::jsonschema::cli::test(
continue;
}

sourcemeta::jsontoolkit::SchemaCompilerTemplate schema_template;
sourcemeta::blaze::Template schema_template;

try {
schema_template = sourcemeta::jsontoolkit::compile(
schema_template = sourcemeta::blaze::compile(
schema.value(), sourcemeta::jsontoolkit::default_schema_walker,
test_resolver, sourcemeta::jsontoolkit::default_schema_compiler);
test_resolver, sourcemeta::blaze::default_schema_compiler);
} catch (const sourcemeta::jsontoolkit::SchemaReferenceError &error) {
if (error.location() == sourcemeta::jsontoolkit::Pointer{"$ref"} &&
error.id() == schema_uri.recompose()) {
Expand Down Expand Up @@ -239,12 +242,11 @@ auto sourcemeta::jsonschema::cli::test(
}

const std::string ref{"$ref"};
sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput output{
schema.value(), {std::cref(ref)}};
const auto case_result{sourcemeta::jsontoolkit::evaluate(
sourcemeta::blaze::ErrorTraceOutput output{schema.value(),
{std::cref(ref)}};
const auto case_result{sourcemeta::blaze::evaluate(
schema_template,
get_data(test_case, entry.first.parent_path(), verbose),
sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode::Fast,
std::ref(output))};

std::ostringstream test_case_description;
Expand Down
30 changes: 13 additions & 17 deletions src/command_validate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include <sourcemeta/jsontoolkit/jsonl.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <sourcemeta/blaze/compiler.h>
#include <sourcemeta/blaze/evaluator.h>

#include <chrono> // std::chrono
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cerr
Expand Down Expand Up @@ -48,9 +51,9 @@ auto sourcemeta::jsonschema::cli::validate(
}

const auto benchmark{options.contains("b") || options.contains("benchmark")};
const auto schema_template{sourcemeta::jsontoolkit::compile(
const auto schema_template{sourcemeta::blaze::compile(
schema, sourcemeta::jsontoolkit::default_schema_walker, custom_resolver,
sourcemeta::jsontoolkit::default_schema_compiler)};
sourcemeta::blaze::default_schema_compiler)};

bool result{true};

Expand All @@ -69,14 +72,12 @@ auto sourcemeta::jsonschema::cli::validate(
for (const auto &instance : sourcemeta::jsontoolkit::JSONL{stream}) {
index += 1;
std::ostringstream error;
sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput output{
instance};
sourcemeta::blaze::ErrorTraceOutput output{instance};
bool subresult = true;
if (benchmark) {
const auto timestamp_start{
std::chrono::high_resolution_clock::now()};
subresult =
sourcemeta::jsontoolkit::evaluate(schema_template, instance);
subresult = sourcemeta::blaze::evaluate(schema_template, instance);
const auto timestamp_end{std::chrono::high_resolution_clock::now()};
const auto duration_us{
std::chrono::duration_cast<std::chrono::microseconds>(
Expand All @@ -87,10 +88,8 @@ auto sourcemeta::jsonschema::cli::validate(
error << "error: Schema validation failure\n";
}
} else {
subresult = sourcemeta::jsontoolkit::evaluate(
schema_template, instance,
sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode::Fast,
std::ref(output));
subresult = sourcemeta::blaze::evaluate(schema_template, instance,
std::ref(output));
}

if (subresult) {
Expand Down Expand Up @@ -125,12 +124,11 @@ auto sourcemeta::jsonschema::cli::validate(
} else {
const auto instance{sourcemeta::jsontoolkit::from_file(instance_path)};
std::ostringstream error;
sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput output{instance};
sourcemeta::blaze::ErrorTraceOutput output{instance};
bool subresult{true};
if (benchmark) {
const auto timestamp_start{std::chrono::high_resolution_clock::now()};
subresult =
sourcemeta::jsontoolkit::evaluate(schema_template, instance);
subresult = sourcemeta::blaze::evaluate(schema_template, instance);
const auto timestamp_end{std::chrono::high_resolution_clock::now()};
const auto duration_us{
std::chrono::duration_cast<std::chrono::microseconds>(
Expand All @@ -142,10 +140,8 @@ auto sourcemeta::jsonschema::cli::validate(
result = false;
}
} else {
subresult = sourcemeta::jsontoolkit::evaluate(
schema_template, instance,
sourcemeta::jsontoolkit::SchemaCompilerEvaluationMode::Fast,
std::ref(output));
subresult = sourcemeta::blaze::evaluate(schema_template, instance,
std::ref(output));
}

if (subresult) {
Expand Down
22 changes: 7 additions & 15 deletions src/utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,8 @@ auto parse_options(const std::span<const std::string> &arguments,
return options;
}

auto print(
const sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput &output,
std::ostream &stream) -> void {
auto print(const sourcemeta::blaze::ErrorTraceOutput &output,
std::ostream &stream) -> void {
stream << "error: Schema validation failure\n";
for (const auto &entry : output) {
stream << " " << entry.message << "\n";
Expand All @@ -193,23 +192,18 @@ auto print(
static auto fallback_resolver(
const std::map<std::string, std::vector<std::string>> &options,
std::string_view identifier)
-> std::future<std::optional<sourcemeta::jsontoolkit::JSON>> {
auto official_result{
sourcemeta::jsontoolkit::official_resolver(identifier).get()};
-> std::optional<sourcemeta::jsontoolkit::JSON> {
auto official_result{sourcemeta::jsontoolkit::official_resolver(identifier)};
if (official_result.has_value()) {
std::promise<std::optional<sourcemeta::jsontoolkit::JSON>> promise;
promise.set_value(std::move(official_result));
return promise.get_future();
return official_result;
}

// If the URI is not an HTTP URL, then abort
const sourcemeta::jsontoolkit::URI uri{std::string{identifier}};
const auto maybe_scheme{uri.scheme()};
if (uri.is_urn() || !maybe_scheme.has_value() ||
(maybe_scheme.value() != "https" && maybe_scheme.value() != "http")) {
std::promise<std::optional<sourcemeta::jsontoolkit::JSON>> promise;
promise.set_value(std::nullopt);
return promise.get_future();
return std::nullopt;
}

log_verbose(options) << "Resolving over HTTP: " << identifier << "\n";
Expand All @@ -222,9 +216,7 @@ static auto fallback_resolver(
throw std::runtime_error(error.str());
}

std::promise<std::optional<sourcemeta::jsontoolkit::JSON>> promise;
promise.set_value(sourcemeta::jsontoolkit::parse(response.body()));
return promise.get_future();
return sourcemeta::jsontoolkit::parse(response.body());
}

auto resolver(const std::map<std::string, std::vector<std::string>> &options,
Expand Down
7 changes: 4 additions & 3 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <sourcemeta/jsontoolkit/jsonpointer.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <sourcemeta/blaze/compiler.h>

#include <filesystem> // std::filesystem
#include <map> // std::map
#include <ostream> // std::ostream
Expand All @@ -27,9 +29,8 @@ auto for_each_json(const std::vector<std::string> &arguments,
-> std::vector<
std::pair<std::filesystem::path, sourcemeta::jsontoolkit::JSON>>;

auto print(
const sourcemeta::jsontoolkit::SchemaCompilerErrorTraceOutput &output,
std::ostream &stream) -> void;
auto print(const sourcemeta::blaze::ErrorTraceOutput &output,
std::ostream &stream) -> void;

auto resolver(const std::map<std::string, std::vector<std::string>> &options,
const bool remote = false)
Expand Down
2 changes: 1 addition & 1 deletion test/compile/pass_1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ cat << 'EOF' > "$TMP/expected.json"
"type": "type",
"value": "string"
},
"schemaResource": "",
"schemaResource": 0,
"absoluteKeywordLocation": "#/properties/foo/type",
"relativeSchemaLocation": "/properties/foo/type",
"relativeInstanceLocation": "/foo",
Expand Down
3 changes: 0 additions & 3 deletions test/validate/fail_2019_09.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ error: Schema validation failure
The value was expected to be of type string but it was of type integer
at instance location "/foo"
at evaluate path "/properties/foo/type"
The object value was expected to validate against the single defined property subschema
at instance location ""
at evaluate path "/properties"
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
3 changes: 0 additions & 3 deletions test/validate/fail_2020_12.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ error: Schema validation failure
The value was expected to be of type string but it was of type integer
at instance location "/foo"
at evaluate path "/properties/foo/type"
The object value was expected to validate against the single defined property subschema
at instance location ""
at evaluate path "/properties"
EOF

diff "$TMP/stderr.txt" "$TMP/expected.txt"
Loading

0 comments on commit 0b140c8

Please sign in to comment.