diff --git a/DEPENDENCIES b/DEPENDENCIES index 027e71aa6..6d4ccc719 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,4 +1,4 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 core https://github.com/sourcemeta/core fbdf367f3b443dd05fdbe7bae6685f86c8332e4f -blaze https://github.com/sourcemeta/blaze ad08d100148b60b03ee5f30c40f8f11ea9a13aca +blaze https://github.com/sourcemeta/blaze 607d122508e165aebddfa4e0c2f73d61cebb1e75 ctrf https://github.com/ctrf-io/ctrf 93ea827d951390190171d37443bff169cf47c808 diff --git a/test/lint/pass_lint_list_exclude.clitest b/test/lint/pass_lint_list_exclude.clitest index 88a5141eb..55ecb96fe 100644 --- a/test/lint/pass_lint_list_exclude.clitest +++ b/test/lint/pass_lint_list_exclude.clitest @@ -304,7 +304,10 @@ WRITE expected_0.txt UNTIL EOF 1> unsatisfiable_min_properties 1> Setting `minProperties` to a number less than `required` does not add any further constraint 1> -1> Number of rules: 101 +1> valid_example +1> Only set an `example` value that validates against the schema +1> +1> Number of rules: 102 EOF COMPARE result_0.txt AGAINST expected_0.txt diff --git a/test/lint/pass_lint_list_long.clitest b/test/lint/pass_lint_list_long.clitest index d9b75aa50..bacbf5e0f 100644 --- a/test/lint/pass_lint_list_long.clitest +++ b/test/lint/pass_lint_list_long.clitest @@ -307,10 +307,13 @@ WRITE expected_0.txt UNTIL EOF 1> valid_default 1> Only set a `default` value that validates against the schema 1> +1> valid_example +1> Only set an `example` value that validates against the schema +1> 1> valid_examples 1> Only include instances in the `examples` array that validate against the schema 1> -1> Number of rules: 103 +1> Number of rules: 104 EOF COMPARE result_0.txt AGAINST expected_0.txt diff --git a/test/lint/pass_lint_list_short.clitest b/test/lint/pass_lint_list_short.clitest index b5b73896c..a477b848d 100644 --- a/test/lint/pass_lint_list_short.clitest +++ b/test/lint/pass_lint_list_short.clitest @@ -307,10 +307,13 @@ WRITE expected_0.txt UNTIL EOF 1> valid_default 1> Only set a `default` value that validates against the schema 1> +1> valid_example +1> Only set an `example` value that validates against the schema +1> 1> valid_examples 1> Only include instances in the `examples` array that validate against the schema 1> -1> Number of rules: 103 +1> Number of rules: 104 EOF COMPARE result_0.txt AGAINST expected_0.txt diff --git a/vendor/blaze/src/alterschema/alterschema.cc b/vendor/blaze/src/alterschema/alterschema.cc index f3739950d..df1acc953 100644 --- a/vendor/blaze/src/alterschema/alterschema.cc +++ b/vendor/blaze/src/alterschema/alterschema.cc @@ -390,6 +390,7 @@ inline auto compile_non_standalone_subschema( #include "linter/unsatisfiable_max_contains.h" #include "linter/unsatisfiable_min_properties.h" #include "linter/valid_default.h" +#include "linter/valid_example.h" #include "linter/valid_examples.h" #undef ONLY_CONTINUE_IF @@ -497,6 +498,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void { bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); } diff --git a/vendor/blaze/src/alterschema/linter/valid_example.h b/vendor/blaze/src/alterschema/linter/valid_example.h new file mode 100644 index 000000000..a42f2b915 --- /dev/null +++ b/vendor/blaze/src/alterschema/linter/valid_example.h @@ -0,0 +1,99 @@ +class ValidExample final : public SchemaTransformRule { +public: + using mutates = std::true_type; + using reframe_after_transform = std::true_type; + ValidExample(Compiler compiler = default_schema_compiler) + : SchemaTransformRule{"valid_example", "Only set an `example` value that " + "validates against the schema"}, + compiler_{std::move(compiler)} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &root, + const sourcemeta::core::SchemaVocabularies &vocabularies, + const sourcemeta::core::SchemaFrame &frame, + const sourcemeta::core::SchemaFrame::Location &location, + const sourcemeta::core::SchemaWalker &walker, + const sourcemeta::core::SchemaResolver &resolver) const + -> SchemaTransformRule::Result override { + using Known = SchemaVocabularies::Known; + ONLY_CONTINUE_IF(vocabularies.contains_any({Known::OPENAPI_3_0_BASE, + Known::OPENAPI_3_1_BASE, + Known::OPENAPI_3_2_BASE}) && + schema.is_object() && schema.defines("example")); + + if (vocabularies.contains(Known::OPENAPI_3_0_BASE)) { + ONLY_CONTINUE_IF(!schema.defines("$ref")); + } + + const auto &instance{schema.at("example")}; + + if (frame.standalone()) { + const auto base{frame.uri(location.pointer)}; + assert(base.has_value()); + Template schema_template; + try { + schema_template = compile(root, walker, resolver, this->compiler_, + frame, base.value().get(), Mode::Exhaustive); + } catch (const CompilerReferenceTargetNotSchemaError &) { + throw; + } catch (const sourcemeta::core::SchemaVocabularyError &) { + throw; + } catch (...) { + return false; + } + SimpleOutput output{instance}; + Evaluator evaluator; + const auto result{ + evaluator.validate(schema_template, instance, std::ref(output))}; + if (result) { + return false; + } + + std::ostringstream message; + for (const auto &entry : output) { + message << entry.message << "\n"; + message << " at instance location \""; + sourcemeta::core::stringify(entry.instance_location, message); + message << "\"\n"; + message << " at evaluate path \""; + sourcemeta::core::stringify(entry.evaluate_path, message); + message << "\"\n"; + } + + return {{{"example"}}, std::move(message).str()}; + } + + sourcemeta::core::WeakPointer base; + const auto schema_template{compile_non_standalone_subschema( + root, frame, location, walker, resolver, this->compiler_, base)}; + ONLY_CONTINUE_IF(schema_template.has_value()); + SimpleOutput output{instance, base}; + Evaluator evaluator; + const auto result{evaluator.validate(schema_template.value(), instance, + std::ref(output))}; + if (result) { + return false; + } + + std::ostringstream message; + for (const auto &entry : output) { + message << entry.message << "\n"; + message << " at instance location \""; + sourcemeta::core::stringify(entry.instance_location, message); + message << "\"\n"; + message << " at evaluate path \""; + sourcemeta::core::stringify(entry.evaluate_path, message); + message << "\"\n"; + } + + return {{{"example"}}, std::move(message).str()}; + } + + auto transform(JSON &schema, const Result &) const -> void override { + schema.erase("example"); + } + +private: + const Compiler compiler_; +};