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

Add an --http option to validate, test, and bundle #19

Merged
merged 1 commit into from
Jun 1, 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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/command_bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@

auto intelligence::jsonschema::cli::bundle(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {})};
const auto options{parse_options(arguments, {"h", "http"})};
CLI_ENSURE(!options.at("").empty(), "You must pass a JSON Schema as input");
auto schema{sourcemeta::jsontoolkit::from_file(options.at("").front())};
sourcemeta::jsontoolkit::bundle(
schema, sourcemeta::jsontoolkit::default_schema_walker, resolver(options))
schema, sourcemeta::jsontoolkit::default_schema_walker,
resolver(options, options.contains("h") || options.contains("http")))
.wait();
sourcemeta::jsontoolkit::prettify(
schema, std::cout, sourcemeta::jsontoolkit::schema_format_compare);
Expand Down
6 changes: 3 additions & 3 deletions src/command_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
#include "utils.h"

// TODO: Add a flag to first validate schema against its metaschema
// TODO: Add a flag to prevent the use of HTTP resolution
auto intelligence::jsonschema::cli::test(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {})};
const auto options{parse_options(arguments, {"h", "http"})};
bool result{true};
const auto test_resolver{resolver(options)};
const auto test_resolver{
resolver(options, options.contains("h") || options.contains("http"))};

for (const auto &entry : for_each_json(options.at(""))) {
const sourcemeta::jsontoolkit::JSON test{
Expand Down
6 changes: 3 additions & 3 deletions src/command_validate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@

// TODO: Add a flag to first validate schema against its metaschema
// TODO: Add a flag to emit output using the standard JSON Schema output format
// TODO: Add a flag to prevent the use of HTTP resolution
auto intelligence::jsonschema::cli::validate(
const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {})};
const auto options{parse_options(arguments, {"h", "http"})};
CLI_ENSURE(options.at("").size() >= 2,
"You must pass a schema followed by an instance")
const auto &schema_path{options.at("").at(0)};
Expand All @@ -23,7 +22,8 @@ auto intelligence::jsonschema::cli::validate(
const auto schema{sourcemeta::jsontoolkit::from_file(schema_path)};

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

const auto instance{sourcemeta::jsontoolkit::from_file(instance_path)};
Expand Down
19 changes: 14 additions & 5 deletions src/main.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <algorithm> // std::min
#include <cstdlib> // EXIT_FAILURE, EXIT_SUCCESS
#include <filesystem> // std::filesystem
Expand All @@ -18,17 +20,20 @@ Global Options:

Commands:

validate <schema.json> <instance.json>
validate <schema.json> <instance.json> [--http/-h]

Validate an instance against a schema, printing error information, if
any, in a human-readable manner.
any, in a human-readable manner. The `--http/-h` option enables resolving
remote schemas over the HTTP protocol.

test [schema.json...]
test [schema.json...] [--http/-h]

A schema test runner inspired by the official JSON Schema test suite.
Passing directories as input will run every `.json` file in such
directory (recursively) as a test. If no argument is passed, run every
`.json` file in the current working directory (recursively) as a test.
The `--http/-h` option enables resolving remote schemas over the HTTP
protocol.

fmt [schema.json...] [--check/-c]

Expand All @@ -46,12 +51,13 @@ Global Options:
(recursively). The `--fix/-f` option will attempt to automatically
fix the linter errors.

bundle <schema.json>
bundle <schema.json> [--http/-h]

Perform JSON Schema Bundling on a schema to inline remote references,
printing the result to standard output. Read
https://json-schema.org/blog/posts/bundling-json-schema-compound-documents
to learn more.
to learn more. The `--http/-h` option enables resolving remote schemas
over the HTTP protocol.

frame <schema.json>

Expand Down Expand Up @@ -91,6 +97,9 @@ auto main(int argc, char *argv[]) noexcept -> int {
const std::vector<std::string> arguments{argv + std::min(2, argc),
argv + argc};
return jsonschema_main(program, command, arguments);
} catch (const sourcemeta::jsontoolkit::SchemaResolutionError &error) {
std::cerr << error.what() << ": " << error.id() << "\n";
return EXIT_FAILURE;
} catch (const std::exception &error) {
std::cerr << "Error: " << error.what() << "\n";
return EXIT_FAILURE;
Expand Down
6 changes: 3 additions & 3 deletions src/utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ static auto fallback_resolver(std::string_view identifier)
return promise.get_future();
}

auto resolver(const std::map<std::string, std::vector<std::string>> &options)
-> sourcemeta::jsontoolkit::SchemaResolver {
auto resolver(const std::map<std::string, std::vector<std::string>> &options,
const bool remote) -> sourcemeta::jsontoolkit::SchemaResolver {
sourcemeta::jsontoolkit::MapSchemaResolver dynamic_resolver{
fallback_resolver};
remote ? fallback_resolver : sourcemeta::jsontoolkit::official_resolver};

if (options.contains("resolve")) {
for (const auto &schema_path : options.at("resolve")) {
Expand Down
3 changes: 2 additions & 1 deletion src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ auto pretty_evaluate_callback(
const sourcemeta::jsontoolkit::JSON &,
const sourcemeta::jsontoolkit::JSON &) -> void;

auto resolver(const std::map<std::string, std::vector<std::string>> &options)
auto resolver(const std::map<std::string, std::vector<std::string>> &options,
const bool remote = false)
-> sourcemeta::jsontoolkit::SchemaResolver;

auto log_verbose(const std::map<std::string, std::vector<std::string>> &options)
Expand Down
5 changes: 5 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ add_jsonschema_test_unix(format_cwd)
add_jsonschema_test_unix(frame)
add_jsonschema_test_unix(validate_pass)
add_jsonschema_test_unix(validate_fail)
add_jsonschema_test_unix(validate_fail_remote_no_http)
add_jsonschema_test_unix(validate_non_supported)
add_jsonschema_test_unix(bundle_non_remote)
add_jsonschema_test_unix(bundle_remote_single_schema)
add_jsonschema_test_unix(bundle_remote_no_http)
add_jsonschema_test_unix(test_single_pass)
add_jsonschema_test_unix(test_single_fail)
add_jsonschema_test_unix(test_single_unsupported)
add_jsonschema_test_unix(lint_pass)
add_jsonschema_test_unix(lint_fail)
add_jsonschema_test_unix(lint_fix)

# TODO: Enable certain tests (if a given CMake option
# is set) that perform outbound HTTP requests
26 changes: 26 additions & 0 deletions test/bundle_remote_no_http.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com",
"$ref": "nested"
}
EOF

"$1" bundle "$TMP/schema.json" && CODE="$?" || CODE="$?"

if [ "$CODE" = "0" ]
then
echo "FAIL" 1>&2
exit 1
else
echo "PASS" 1>&2
fi
30 changes: 30 additions & 0 deletions test/validate_fail_remote_no_http.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com",
"$ref": "nested"
}
EOF

cat << 'EOF' > "$TMP/instance.json"
{ "foo": 1 }
EOF

"$1" validate "$TMP/schema.json" "$TMP/instance.json" && CODE="$?" || CODE="$?"

if [ "$CODE" = "0" ]
then
echo "FAIL" 1>&2
exit 1
else
echo "PASS" 1>&2
fi
Loading