From 86e97231bc3549b767040166f794ca0400b42672 Mon Sep 17 00:00:00 2001 From: andrejlevkovitch Date: Fri, 29 May 2020 16:21:01 +0300 Subject: [PATCH] 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; }