From 80333cda2bdae59edd4731bc7b2a91db3ef4e635 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 23 May 2020 09:04:42 +0200 Subject: [PATCH 1/5] bump to 2.1.1 for new developments --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4cc218..aae7ab3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ project(nlohmann_json_schema_validator - LANGUAGES CXX) + LANGUAGES CXX) -set(PROJECT_VERSION 2.1.0) +set(PROJECT_VERSION 2.1.1) cmake_minimum_required(VERSION 3.2) -option(BUILD_TESTS "Build tests" ON) -option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_TESTS "Build tests" ON) +option(BUILD_EXAMPLES "Build examples" ON) # the library add_library(nlohmann_json_schema_validator From a3f3a59a0ec29e5e4ec6467cf4c0b6ee82af1812 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 23 May 2020 10:14:10 +0200 Subject: [PATCH 2/5] add content-checker-callback Used when contentEncoding or mediaType attributes are present on a string-value. --- src/json-validator.cpp | 69 ++++++++++++++++--- src/nlohmann/json-schema.hpp | 7 +- test/JSON-Schema-Test-Suite/CMakeLists.txt | 1 - .../json-schema-test.cpp | 47 ++++++++++++- 4 files changed, 111 insertions(+), 13 deletions(-) diff --git a/src/json-validator.cpp b/src/json-validator.cpp index f16f3ef..d956037 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -105,6 +105,7 @@ class root_schema : public schema { schema_loader loader_; format_checker format_check_; + content_checker content_check_; std::shared_ptr root_; @@ -128,10 +129,18 @@ class root_schema : public schema public: root_schema(schema_loader &&loader, - format_checker &&format) - : schema(this), loader_(std::move(loader)), format_check_(std::move(format)) {} + format_checker &&format, + content_checker &&content) + + : schema(this), + loader_(std::move(loader)), + format_check_(std::move(format)), + content_check_(std::move(content)) + { + } format_checker &format_check() { return format_check_; } + content_checker &content_check() { return content_check_; } void insert(const json_uri &uri, const std::shared_ptr &s) { @@ -461,8 +470,24 @@ class type_schema : public schema else_->validate(ptr, instance, patch, e); } } + + // special treatment + if (instance.type() == json::value_t::string && std::get<0>(content_)) { + if (root_->content_check() == nullptr) + e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'"); + else { + try { + root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance); + } catch (const std::exception &ex) { + e.error(ptr, instance, std::string("content-checking failed: ") + ex.what()); + } + } + } } +protected: + std::tuple content_{false, "", ""}; + public: type_schema(json &sch, root_schema *root, @@ -583,6 +608,20 @@ public: } sch.erase(attr); } + + attr = sch.find("contentEncoding"); + if (attr != sch.end()) { + std::get<0>(content_) = true; + std::get<1>(content_) = attr.value().get(); + sch.erase(attr); + } + + attr = sch.find("contentMediaType"); + if (attr != sch.end()) { + std::get<0>(content_) = true; + std::get<2>(content_) = attr.value().get(); + sch.erase(attr); + } } }; @@ -1209,19 +1248,33 @@ namespace json_schema { json_validator::json_validator(schema_loader loader, - format_checker format) - : root_(std::unique_ptr(new root_schema(std::move(loader), std::move(format)))) + format_checker format, + content_checker content) + : root_(std::unique_ptr(new root_schema(std::move(loader), + std::move(format), + std::move(content)))) { } -json_validator::json_validator(const json &schema, schema_loader loader, format_checker format) - : json_validator(std::move(loader), std::move(format)) +json_validator::json_validator(const json &schema, + schema_loader loader, + format_checker format, + content_checker content) + : json_validator(std::move(loader), + std::move(format), + std::move(content)) { set_root_schema(schema); } -json_validator::json_validator(json &&schema, schema_loader loader, format_checker format) - : json_validator(std::move(loader), std::move(format)) +json_validator::json_validator(json &&schema, + schema_loader loader, + format_checker format, + content_checker content) + + : json_validator(std::move(loader), + std::move(format), + std::move(content)) { set_root_schema(std::move(schema)); } diff --git a/src/nlohmann/json-schema.hpp b/src/nlohmann/json-schema.hpp index baa9719..c25ca72 100644 --- a/src/nlohmann/json-schema.hpp +++ b/src/nlohmann/json-schema.hpp @@ -133,6 +133,7 @@ extern json draft7_schema_builtin; typedef std::function schema_loader; typedef std::function format_checker; +typedef std::function content_checker; // Interface for validation error handlers class JSON_SCHEMA_VALIDATOR_API error_handler @@ -168,10 +169,10 @@ class JSON_SCHEMA_VALIDATOR_API json_validator std::unique_ptr root_; public: - json_validator(schema_loader = nullptr, format_checker = nullptr); + json_validator(schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr); - json_validator(const json &, schema_loader = nullptr, format_checker = nullptr); - json_validator(json &&, schema_loader = nullptr, format_checker = nullptr); + json_validator(const json &, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr); + json_validator(json &&, schema_loader = nullptr, format_checker = nullptr, content_checker = nullptr); json_validator(json_validator &&); json_validator &operator=(json_validator &&); diff --git a/test/JSON-Schema-Test-Suite/CMakeLists.txt b/test/JSON-Schema-Test-Suite/CMakeLists.txt index fc52df2..61d5fd4 100644 --- a/test/JSON-Schema-Test-Suite/CMakeLists.txt +++ b/test/JSON-Schema-Test-Suite/CMakeLists.txt @@ -50,7 +50,6 @@ if(JSON_SCHEMA_TEST_SUITE_PATH) # some optional tests will fail set_tests_properties( JSON-Suite::Optional::bignum - JSON-Suite::Optional::content JSON-Suite::Optional::zeroTerminatedFloats JSON-Suite::Optional::non-bmp-regex diff --git a/test/JSON-Schema-Test-Suite/json-schema-test.cpp b/test/JSON-Schema-Test-Suite/json-schema-test.cpp index 05a255d..deee4c8 100644 --- a/test/JSON-Schema-Test-Suite/json-schema-test.cpp +++ b/test/JSON-Schema-Test-Suite/json-schema-test.cpp @@ -39,6 +39,50 @@ static void loader(const json_uri &uri, json &schema) } } +// from here +// https://stackoverflow.com/a/34571089/880584 +static std::string base64_decode(const std::string &in) +{ + std::string out; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) + T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + + unsigned val = 0; + int valb = -8; + for (uint8_t c : in) { + if (c == '=') + break; + + if (T[c] == -1) { + throw std::invalid_argument("base64-decode: unexpected character in encode string: '" + std::string(1, c) + "'"); + } + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; +} + +static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance) +{ + std::string content = instance; + + if (contentEncoding == "base64") + content = base64_decode(instance); + else if (contentEncoding != "") + throw std::invalid_argument("unable to check for contentEncoding '" + contentEncoding + "'"); + + if (contentMediaType == "application/json") + auto dummy = json::parse(content); // throws if conversion fails + else if (contentMediaType != "") + throw std::invalid_argument("unable to check for contentMediaType '" + contentMediaType + "'"); +} + int main(void) { json validation; // a validation case following the JSON-test-suite-schema @@ -62,7 +106,8 @@ int main(void) const auto &schema = test_group["schema"]; json_validator validator(loader, - nlohmann::json_schema::default_string_format_check); + nlohmann::json_schema::default_string_format_check, + content); validator.set_root_schema(schema); From 06540818c32b69279f9a65c07e236564043b8c40 Mon Sep 17 00:00:00 2001 From: andrejlevkovitch Date: Fri, 22 May 2020 11:50:26 +0300 Subject: [PATCH 3/5] add support for validate binary data in nlohmann::json Used for bson or other implementations of binary json-instances. --- src/json-validator.cpp | 32 ++++++- test/CMakeLists.txt | 5 + test/binary-validation.cpp | 182 +++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 test/binary-validation.cpp diff --git a/src/json-validator.cpp b/src/json-validator.cpp index d956037..668390a 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -472,7 +472,8 @@ class type_schema : public schema } // special treatment - if (instance.type() == json::value_t::string && std::get<0>(content_)) { + if ((instance.type() == json::value_t::string || instance.type() == json::value_t::binary) && + std::get<0>(content_)) { if (root_->content_check() == nullptr) e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'"); else { @@ -613,6 +614,27 @@ public: if (attr != sch.end()) { std::get<0>(content_) = true; std::get<1>(content_) = attr.value().get(); + + // special case for nlohmann::json-binary-types + // + // https://github.com/pboettch/json-schema-validator/pull/114 + // + // We cannot use explicitly in a schema: {"type": "binary"} or + // "type": ["binary", "number"] we have to be implicit. For a + // schema where "contentEncoding" is set to "binary", an instance + // of type json::value_t::binary is accepted. If a + // contentEncoding-callback has to be provided and is called + // accordingly. For encoding=binary, no other type validations are done + + if (attr.value() == "binary") { + // clear out all other type-schemas + for (auto &type_valid : type_) + type_valid = nullptr; + + // when no schema-type is explicitly given, we accept binary-values + type_[(uint8_t) json::value_t::binary] = type_schema::make(sch, json::value_t::binary, root, uris, known_keywords); + } + sch.erase(attr); } @@ -1154,6 +1176,14 @@ std::shared_ptr type_schema::make(json &schema, case json::value_t::discarded: // not a real type - silence please break; + + case json::value_t::binary: { + // can be used for validate bson or other binary representation of json + // - specific to nlohmann::json - this type is not standardized + // json-schema-draft-7 + json tmp = true; + return std::make_shared(tmp, root); // always true - content-check with do the work + } } return nullptr; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d67e90..f268219 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,3 +55,8 @@ add_executable(json-patch json-patch.cpp) target_include_directories(json-patch PRIVATE ${PROJECT_SOURCE_DIR}/src) target_link_libraries(json-patch nlohmann_json_schema_validator) add_test(NAME json-patch COMMAND json-patch) + +add_executable(binary-validation binary-validation.cpp) +target_include_directories(binary-validation PRIVATE ${PROJECT_SOURCE_DIR}/src) +target_link_libraries(binary-validation PRIVATE nlohmann_json_schema_validator) +add_test(NAME binary-validation COMMAND binary-validation) diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp new file mode 100644 index 0000000..d5ae031 --- /dev/null +++ b/test/binary-validation.cpp @@ -0,0 +1,182 @@ +// bson-validate.cpp + +#include +#include +#include + +static int error_count = 0; + +#define EXPECT_EQ(a, b) \ + do { \ + if (a != b) { \ + std::cerr << "Failed: '" << a << "' != '" << b << "'\n"; \ + error_count++; \ + } \ + } while (0) + +using json = nlohmann::json; +using validator = nlohmann::json_schema::json_validator; + +// check binary data validation +const json bson_schema = json::parse(R"( +{ + "type": "object", + "properties": { + "standard_string": { + "type": "string" + }, + "binary_data": { + "type": "string", + "contentEncoding": "binary" + } + }, + "additionalProperties": false +} +)"); + +const json array_of_types = json::parse(R"( +{ + "type": "object", + "properties": { + "something": { + "type": ["string", "number", "boolean"], + "contentEncoding": "binary" + } + } +} +)"); + +const json array_of_types_without_binary = json::parse(R"( +{ + "type": "object", + "properties": { + "something": { + "type": ["string", "number", "boolean"] + } + } +} +)"); + +class store_ptr_err_handler : public nlohmann::json_schema::basic_error_handler +{ + void error(const nlohmann::json::json_pointer &ptr, const json &, const std::string &message) override + { + nlohmann::json_schema::basic_error_handler::error(ptr, "", message); + std::cerr << "ERROR: '" << ptr << "' - '" + << "" + << "': " << message << "\n"; + failed_pointers.push_back(ptr); + } + +public: + std::vector failed_pointers; + + void reset() override + { + nlohmann::json_schema::basic_error_handler::reset(); + failed_pointers.clear(); + } +}; + +static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance) +{ + if (instance.type() != json::value_t::binary) + throw std::invalid_argument("invalid instance type for binary content checker"); + + std::cerr << "mediaType: '" << contentMediaType << "', encoding: '" << contentEncoding << "'\n"; + + if (contentMediaType != "") + throw std::invalid_argument("unable to check for contentMediaType " + contentMediaType); + + if (contentEncoding == "binary") { + + } else if (contentEncoding != "") + throw std::invalid_argument("unable to check for contentEncoding " + contentEncoding); +} + +int main() +{ + validator val(nullptr, nullptr, content); + + // create some bson doc + json::binary_t arr; + std::string as_binary = "hello world"; + std::copy(as_binary.begin(), as_binary.end(), std::back_inserter(arr)); + + json binary = json::binary(arr); + + store_ptr_err_handler err; + + ///////////////////////////////////// + val.set_root_schema(bson_schema); + + // all right + val.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 0); + err.reset(); + + // invalid binary data + val.validate({{"binary_data", "string, but expect binary data"}}, err); + EXPECT_EQ(err.failed_pointers.size(), 2); + EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); + EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker + err.reset(); + + // also check that simple string not accept binary data + val.validate({{"standard_string", binary}, {"binary_data", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 1); + EXPECT_EQ(err.failed_pointers[0].to_string(), "/standard_string"); + err.reset(); + + ///////////////////////////////////// + // check with array of types + + // check simple types + val.set_root_schema(array_of_types); + val.validate({{"something", "string"}}, err); + val.validate({{"something", 1}}, err); + val.validate({{"something", false}}, err); + EXPECT_EQ(err.failed_pointers.size(), 4); // binary encoding invalidated all other types + err.reset(); + + // check binary data + val.validate({{"something", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 0); + err.reset(); + + ///////////////////////////////////// + // and check that you can't set binary data if contentEncoding don't set + val.set_root_schema(array_of_types_without_binary); + val.validate({{"something", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 1); + EXPECT_EQ(err.failed_pointers[0], "/something"); + err.reset(); + + // check without content-callback everything fails + validator val_no_content; + + ///////////////////////////////////// + val_no_content.set_root_schema(bson_schema); + + // all right + val_no_content.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 1); + err.reset(); + + // invalid binary data + val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err); + EXPECT_EQ(err.failed_pointers.size(), 2); + EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); + EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker + err.reset(); + + // also check that simple string not accept binary data + val_no_content.validate({{"standard_string", binary}, {"binary_data", binary}}, err); + EXPECT_EQ(err.failed_pointers.size(), 2); + EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); + EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string"); + err.reset(); + + + return error_count; +} From 86e97231bc3549b767040166f794ca0400b42672 Mon Sep 17 00:00:00 2001 From: andrejlevkovitch Date: Fri, 29 May 2020 16:21:01 +0300 Subject: [PATCH 4/5] move checks contentEncoding and contentMediaType to string schema --- src/json-validator.cpp | 114 ++++++++++++++++++------------------- test/binary-validation.cpp | 31 +++++----- 2 files changed, 69 insertions(+), 76 deletions(-) diff --git a/src/json-validator.cpp b/src/json-validator.cpp index 668390a..8fc6f6f 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -470,25 +470,8 @@ class type_schema : public schema else_->validate(ptr, instance, patch, e); } } - - // special treatment - if ((instance.type() == json::value_t::string || instance.type() == json::value_t::binary) && - std::get<0>(content_)) { - if (root_->content_check() == nullptr) - e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'"); - else { - try { - root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance); - } catch (const std::exception &ex) { - e.error(ptr, instance, std::string("content-checking failed: ") + ex.what()); - } - } - } } -protected: - std::tuple content_{false, "", ""}; - public: type_schema(json &sch, root_schema *root, @@ -553,6 +536,11 @@ public: // we stick with JSON-schema: use the integer-validator if instance-value is unsigned type_[(uint8_t) json::value_t::number_unsigned] = type_[(uint8_t) json::value_t::number_integer]; + // special for binary types + if (type_[(uint8_t) json::value_t::string]) { + type_[(uint8_t) json::value_t::binary] = type_[(uint8_t) json::value_t::string]; + } + attr = sch.find("enum"); if (attr != sch.end()) { enum_ = {true, attr.value()}; @@ -609,41 +597,6 @@ public: } sch.erase(attr); } - - attr = sch.find("contentEncoding"); - if (attr != sch.end()) { - std::get<0>(content_) = true; - std::get<1>(content_) = attr.value().get(); - - // special case for nlohmann::json-binary-types - // - // https://github.com/pboettch/json-schema-validator/pull/114 - // - // We cannot use explicitly in a schema: {"type": "binary"} or - // "type": ["binary", "number"] we have to be implicit. For a - // schema where "contentEncoding" is set to "binary", an instance - // of type json::value_t::binary is accepted. If a - // contentEncoding-callback has to be provided and is called - // accordingly. For encoding=binary, no other type validations are done - - if (attr.value() == "binary") { - // clear out all other type-schemas - for (auto &type_valid : type_) - type_valid = nullptr; - - // when no schema-type is explicitly given, we accept binary-values - type_[(uint8_t) json::value_t::binary] = type_schema::make(sch, json::value_t::binary, root, uris, known_keywords); - } - - sch.erase(attr); - } - - attr = sch.find("contentMediaType"); - if (attr != sch.end()) { - std::get<0>(content_) = true; - std::get<2>(content_) = attr.value().get(); - sch.erase(attr); - } } }; @@ -658,11 +611,12 @@ class string : public schema #endif std::pair format_; + std::tuple content_{false, "", ""}; std::size_t utf8_length(const std::string &s) const { size_t len = 0; - for (const unsigned char &c : s) + for (unsigned char c : s) if ((c & 0xc0) != 0x80) len++; return len; @@ -686,6 +640,24 @@ class string : public schema } } + if (std::get<0>(content_)) { + if (root_->content_check() == nullptr) + e.error(ptr, instance, std::string("a content checker was not provided but a contentEncoding or contentMediaType for this string have been present: '") + std::get<1>(content_) + "' '" + std::get<2>(content_) + "'"); + else { + try { + root_->content_check()(std::get<1>(content_), std::get<2>(content_), instance); + } catch (const std::exception &ex) { + e.error(ptr, instance, std::string("content-checking failed: ") + ex.what()); + } + } + } else if (instance.type() == json::value_t::binary) { + e.error(ptr, instance, "expected string, but get binary data"); + } + + if (instance.type() != json::value_t::string) { + return; // next checks only for strings + } + #ifndef NO_STD_REGEX if (pattern_.first && !REGEX_NAMESPACE::regex_search(instance.get(), pattern_.second)) @@ -721,6 +693,33 @@ public: sch.erase(attr); } + attr = sch.find("contentEncoding"); + if (attr != sch.end()) { + std::get<0>(content_) = true; + std::get<1>(content_) = attr.value().get(); + + // special case for nlohmann::json-binary-types + // + // https://github.com/pboettch/json-schema-validator/pull/114 + // + // We cannot use explicitly in a schema: {"type": "binary"} or + // "type": ["binary", "number"] we have to be implicit. For a + // schema where "contentEncoding" is set to "binary", an instance + // of type json::value_t::binary is accepted. If a + // contentEncoding-callback has to be provided and is called + // accordingly. For encoding=binary, no other type validations are done + + sch.erase(attr); + } + + attr = sch.find("contentMediaType"); + if (attr != sch.end()) { + std::get<0>(content_) = true; + std::get<2>(content_) = attr.value().get(); + + sch.erase(attr); + } + #ifndef NO_STD_REGEX attr = sch.find("pattern"); if (attr != sch.end()) { @@ -1177,13 +1176,8 @@ std::shared_ptr type_schema::make(json &schema, case json::value_t::discarded: // not a real type - silence please break; - case json::value_t::binary: { - // can be used for validate bson or other binary representation of json - // - specific to nlohmann::json - this type is not standardized - // json-schema-draft-7 - json tmp = true; - return std::make_shared(tmp, root); // always true - content-check with do the work - } + case json::value_t::binary: + break; } return nullptr; } diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp index d5ae031..fda5afa 100644 --- a/test/binary-validation.cpp +++ b/test/binary-validation.cpp @@ -80,18 +80,17 @@ public: static void content(const std::string &contentEncoding, const std::string &contentMediaType, const json &instance) { - if (instance.type() != json::value_t::binary) - throw std::invalid_argument("invalid instance type for binary content checker"); - std::cerr << "mediaType: '" << contentMediaType << "', encoding: '" << contentEncoding << "'\n"; - if (contentMediaType != "") - throw std::invalid_argument("unable to check for contentMediaType " + contentMediaType); - if (contentEncoding == "binary") { - - } else if (contentEncoding != "") - throw std::invalid_argument("unable to check for contentEncoding " + contentEncoding); + if (instance.type() != json::value_t::binary) { + throw std::invalid_argument{"expected binary data"}; + } + } else { + if (instance.type() == json::value_t::binary) { + throw std::invalid_argument{"expected string, but get binary"}; + } + } } int main() @@ -117,9 +116,8 @@ int main() // invalid binary data val.validate({{"binary_data", "string, but expect binary data"}}, err); - EXPECT_EQ(err.failed_pointers.size(), 2); + EXPECT_EQ(err.failed_pointers.size(), 1); EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); - EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker err.reset(); // also check that simple string not accept binary data @@ -133,10 +131,13 @@ int main() // check simple types val.set_root_schema(array_of_types); - val.validate({{"something", "string"}}, err); val.validate({{"something", 1}}, err); val.validate({{"something", false}}, err); - EXPECT_EQ(err.failed_pointers.size(), 4); // binary encoding invalidated all other types + // TODO when we set `string` in array and set `contentEncoding` = "binary" - what it means? We expected string or binary? + // Or we expect only binary? Now if you set `contentEncoding` = "binary", then it means that you expect only binary data, + // not string + //val.validate({{"something", "string"}}, err); -> produce error about type + EXPECT_EQ(err.failed_pointers.size(), 0); err.reset(); // check binary data @@ -165,9 +166,8 @@ int main() // invalid binary data val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err); - EXPECT_EQ(err.failed_pointers.size(), 2); + EXPECT_EQ(err.failed_pointers.size(), 1); EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); - EXPECT_EQ(err.failed_pointers[1].to_string(), "/binary_data"); // second error comes from content-checker err.reset(); // also check that simple string not accept binary data @@ -177,6 +177,5 @@ int main() EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string"); err.reset(); - return error_count; } From f46940990e807337483fffc85c8b92e29e7b145a Mon Sep 17 00:00:00 2001 From: andrejlevkovitch Date: Fri, 29 May 2020 17:05:54 +0300 Subject: [PATCH 5/5] add checks about content checker at shema parsing stage --- src/json-validator.cpp | 4 ++++ test/binary-validation.cpp | 36 +++++++++++++++--------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/json-validator.cpp b/src/json-validator.cpp index 8fc6f6f..9c5fc26 100644 --- a/src/json-validator.cpp +++ b/src/json-validator.cpp @@ -720,6 +720,10 @@ public: sch.erase(attr); } + if (std::get<0>(content_) == true && root_->content_check() == nullptr) { + throw std::invalid_argument{"schema contains contentEncoding/contentMediaType but content checker was not set"}; + } + #ifndef NO_STD_REGEX attr = sch.find("pattern"); if (attr != sch.end()) { diff --git a/test/binary-validation.cpp b/test/binary-validation.cpp index fda5afa..01392cf 100644 --- a/test/binary-validation.cpp +++ b/test/binary-validation.cpp @@ -14,6 +14,19 @@ static int error_count = 0; } \ } while (0) +#define EXPECT_THROW(foo) \ + { \ + bool ok = false; \ + try { \ + foo; \ + } catch (std::exception &) { \ + ok = true; \ + } \ + if (ok == false) { \ + error_count++; \ + } \ + } + using json = nlohmann::json; using validator = nlohmann::json_schema::json_validator; @@ -153,29 +166,10 @@ int main() EXPECT_EQ(err.failed_pointers[0], "/something"); err.reset(); - // check without content-callback everything fails + // check that without content callback you get exception with schema with contentEncoding or contentMeditType validator val_no_content; - ///////////////////////////////////// - val_no_content.set_root_schema(bson_schema); - - // all right - val_no_content.validate({{"standard_string", "some string"}, {"binary_data", binary}}, err); - EXPECT_EQ(err.failed_pointers.size(), 1); - err.reset(); - - // invalid binary data - val_no_content.validate({{"binary_data", "string, but expect binary data"}}, err); - EXPECT_EQ(err.failed_pointers.size(), 1); - EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); - err.reset(); - - // also check that simple string not accept binary data - val_no_content.validate({{"standard_string", binary}, {"binary_data", binary}}, err); - EXPECT_EQ(err.failed_pointers.size(), 2); - EXPECT_EQ(err.failed_pointers[0].to_string(), "/binary_data"); - EXPECT_EQ(err.failed_pointers[1].to_string(), "/standard_string"); - err.reset(); + EXPECT_THROW(val_no_content.set_root_schema(bson_schema)); return error_count; }