Skip to content
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

Make use of JSON Schema validation from the new Blaze project #178

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
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