Skip to content

Commit

Permalink
improve validation (arangodb#11264)
Browse files Browse the repository at this point in the history
  • Loading branch information
ObiWahn authored Mar 18, 2020
1 parent 56ff354 commit 7689112
Show file tree
Hide file tree
Showing 21 changed files with 385 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ void from_value(Consumer& consumer,
const VPackSlice& slice,
VPackOptions const* options,
VPackSlice const* base,
bool ignore_special) {
arangodb::validation::SpecialProperties special) {

switch (slice.type()) {
case arangodb::velocypack::ValueType::Illegal:
throw std::logic_error("unable to produce events from illegal values");
Expand Down Expand Up @@ -52,7 +53,8 @@ void from_value(Consumer& consumer,

case arangodb::velocypack::ValueType::String: {
auto ref = slice.stringRef();
consumer.string(std::string_view(ref.data(), ref.length()));
auto view = std::string_view(ref.data(), ref.length());
consumer.string(view);
return;
}

Expand All @@ -66,7 +68,7 @@ void from_value(Consumer& consumer,
case arangodb::velocypack::ValueType::Array: {
consumer.begin_array(slice.length());
for (const auto& element : VPackArrayIterator(slice)) {
Recurse(consumer, element, options, &slice, ignore_special);
Recurse(consumer, element, options, &slice, special);
consumer.element();
}
consumer.end_array(slice.length());
Expand All @@ -79,14 +81,52 @@ void from_value(Consumer& consumer,
for (const auto& element : VPackObjectIterator(slice)) {
VPackSlice key = element.key;
if (key.isString()) {
auto key_ref = key.stringRef();
consumer.key(std::string_view(key_ref.data(), key_ref.length()));
Recurse(consumer, element.value, options, &slice, ignore_special);
auto ref = key.stringRef();
auto view = std::string_view(ref.data(), ref.length());
if (skip_special(view, special)) {
continue;
}
consumer.key(std::string_view(view));
Recurse(consumer, element.value, options, &slice, special);
consumer.member();
} else if (!ignore_special) {
} else if (special != arangodb::validation::SpecialProperties::None) {
std::string translated_key;
using namespace arangodb::basics;
uint8_t which = static_cast<uint8_t>(key.getUInt()) + VelocyPackHelper::AttributeBase;

switch (which) {
case VelocyPackHelper::KeyAttribute: {
if (!enum_contains_value(special, arangodb::validation::SpecialProperties::Key)) {
continue;
}
break;
}
case VelocyPackHelper::IdAttribute: {
if (!enum_contains_value(special, arangodb::validation::SpecialProperties::Id)) {
continue;
}
break;
}
case VelocyPackHelper::RevAttribute: {
if (!enum_contains_value(special, arangodb::validation::SpecialProperties::Rev)) {
continue;
}
break;
}
case VelocyPackHelper::FromAttribute: {
if (!enum_contains_value(special, arangodb::validation::SpecialProperties::From)) {
continue;
}
break;
}
case VelocyPackHelper::ToAttribute: {
if (!enum_contains_value(special, arangodb::validation::SpecialProperties::To)) {
continue;
}
break;
}
}

consumer.key(arangodb::validation::id_to_string(which));
VPackSlice val = VPackSlice(element.key.begin() + 1);
arangodb::velocypack::ValueLength length;
Expand All @@ -95,19 +135,16 @@ void from_value(Consumer& consumer,
char const* pointer = val.getString(length);
consumer.string(std::string_view(pointer, length));
} else {
Recurse(consumer, val, options, &slice, ignore_special);
Recurse(consumer, val, options, &slice, special);
}
} // ! ignore_special
}
consumer.end_object(len);
return;
}
case VPackValueType::External: {
Recurse(consumer,
VPackSlice(reinterpret_cast<uint8_t const*>(slice.getExternal())),
options,
base,
ignore_special);
Recurse(
consumer, VPackSlice(reinterpret_cast<uint8_t const*>(slice.getExternal())), options, base, special);
return;
}

Expand Down Expand Up @@ -137,12 +174,12 @@ void from_value(Consumer& consumer,
const VPackSlice& slice,
VPackOptions const* options,
VPackSlice const* base,
bool ignore_special) {
arangodb::validation::SpecialProperties special) {
// clang-format off
from_value<static_cast<void (*)(Consumer&, const VPackSlice&, VPackOptions const*, VPackSlice const*, bool)>(&from_value<Traits, Consumer>)
from_value<static_cast<void (*)(Consumer&, const VPackSlice&, VPackOptions const*, VPackSlice const*, arangodb::validation::SpecialProperties)>(&from_value<Traits, Consumer>)
,Traits
,Consumer
>(consumer, slice, options, base, ignore_special);
>(consumer, slice, options, base, special);
// clang-format on
}

Expand Down
22 changes: 21 additions & 1 deletion 3rdParty/json-schema-validation/include/validation/types.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef ARANGO_SCHEMA_VALIADTION_TYPES_HEADER
#define ARANGO_SCHEMA_VALIADTION_TYPES_HEADER 1

#if __has_include("Basics/VelocyPackHelper")
#if __has_include("Basics/VelocyPackHelper.h")
#include "Basics/VelocyPackHelper.h"
#else
#include <cstdint>
Expand All @@ -21,8 +21,28 @@ class VelocyPackHelper {

#include <string>
namespace arangodb::validation {

enum class SpecialProperties {
None = 0,
Key = 1,
Id = 2,
Rev = 4,
From = 8,
To = 16,
All = 31,
};

[[nodiscard]] std::string const& id_to_string(std::uint8_t);

[[nodiscard]] inline bool enum_contains_value(SpecialProperties tested, SpecialProperties test_for) {
auto tested_int = static_cast<std::underlying_type_t<SpecialProperties>>(tested);
auto test_for_int = static_cast<std::underlying_type_t<SpecialProperties>>(test_for);
return tested_int & test_for_int;
}

[[nodiscard]] SpecialProperties view_to_special(std::string_view);
[[nodiscard]] bool skip_special(std::string_view, SpecialProperties);

} // namespace arangodb::validation

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>

#include "types.hpp"
#include <tao/json/from_file.hpp>

namespace tao::json {
template<template<typename...> class Traits>
class basic_schema;
}

namespace arangodb::validation {
[[nodiscard]] bool validate(tao::json::value const& doc,
tao::json::basic_schema<tao::json::traits> const& schema); // first step
[[nodiscard]] bool validate(VPackSlice const doc,
VPackOptions const*,
tao::json::basic_schema<tao::json::traits> const& schema); // final
[[nodiscard]] bool validate(tao::json::basic_schema<tao::json::traits> const& schema,
SpecialProperties special,
VPackSlice const doc,
VPackOptions const*);


[[nodiscard]] tao::json::value slice_to_value(VPackSlice const& doc,
bool ingore_special = false,
SpecialProperties special = SpecialProperties::None,
VPackOptions const* options = &VPackOptions::Defaults,
VPackSlice const* = nullptr);

Expand Down
70 changes: 54 additions & 16 deletions 3rdParty/json-schema-validation/src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace {
using namespace arangodb;
using namespace arangodb::validation;
[[nodiscard]] tao::json::value slice_object_to_value(VPackSlice const& slice,
bool ignore_special,
SpecialProperties special,
VPackOptions const* options,
VPackSlice const* base) {
assert(slice.isObject());
Expand All @@ -27,8 +27,8 @@ using namespace arangodb::validation;

if (key.isString()) {
// regular attribute
rv.try_emplace(key.copyString(), slice_to_value(it.value(), ignore_special, options, &slice));
} else if (!ignore_special) {
rv.try_emplace(key.copyString(), slice_to_value(it.value(), special, options, &slice));
} else if (special != SpecialProperties::None) {
// optimized code path for translated system attributes
tao::json::value sub;
VPackSlice val = VPackSlice(key.begin() + 1);
Expand All @@ -37,7 +37,7 @@ using namespace arangodb::validation;
char const* pointer = val.getString(length);
sub = std::string(pointer, length);
} else {
sub = slice_to_value(val, ignore_special, options, &slice);
sub = slice_to_value(val, special, options, &slice);
}

using namespace arangodb::basics;
Expand All @@ -52,7 +52,7 @@ using namespace arangodb::validation;
}

[[nodiscard]] tao::json::value slice_array_to_value(VPackSlice const& slice,
bool ignore_special,
SpecialProperties special,
VPackOptions const* options,
VPackSlice const* base) {
assert(slice.isArray());
Expand All @@ -62,7 +62,7 @@ using namespace arangodb::validation;
a.resize(it.size());

while (it.valid()) {
tao::json::value val = slice_to_value(it.value(), ignore_special, options, &slice);
tao::json::value val = slice_to_value(it.value(), special, options, &slice);
rv.push_back(std::move(val));
it.next();
}
Expand All @@ -72,11 +72,11 @@ using namespace arangodb::validation;

template<template<typename...> class Traits>
[[nodiscard]] bool validate(const tao::json::basic_schema<Traits>& schema,
SpecialProperties special,
VPackSlice const& v,
VPackOptions const* options = &VPackOptions::Defaults,
bool ignore_special = true) {
VPackOptions const* options = &VPackOptions::Defaults) {
const auto c = schema.consumer();
tao::json::events::from_value<Traits>(*c, v, options, nullptr, ignore_special);
tao::json::events::from_value<Traits>(*c, v, options, nullptr, special);
return c->finalize();
}

Expand All @@ -90,17 +90,55 @@ std::string const to_string = "_to";
} // namespace

namespace arangodb::validation {
SpecialProperties view_to_special(std::string_view view) {
if (view == key_string) {
return SpecialProperties::Key;
} else if (view == id_string) {
return SpecialProperties::Id;
} else if (view == rev_string) {
return SpecialProperties::Rev;
} else if (view == from_string) {
return SpecialProperties::From;
} else if (view == to_string) {
return SpecialProperties::To;
} else {
return SpecialProperties::None;
}
}

bool skip_special(std::string_view view, SpecialProperties special) {
auto len = view.length();
if (len < 3 || len > 5) {
return false;
}

auto search = view_to_special(view);
if (search == SpecialProperties::None) {
return false;
}

if (enum_contains_value(special, search)) {
return false;
}

return true;
}

bool validate(VPackSlice const doc, VPackOptions const* options, tao::json::schema const& schema) {
return ::validate(schema, doc, options, true);
bool validate(tao::json::schema const& schema,
SpecialProperties special,
VPackSlice const doc,
VPackOptions const* options) {
return ::validate(schema, special, doc, options);
}

bool validate(tao::json::value const& doc, tao::json::schema const& schema) {
return schema.validate(doc);
}

tao::json::value
slice_to_value(VPackSlice const& slice, bool ignore_special, VPackOptions const* options, VPackSlice const* base) {
tao::json::value slice_to_value(VPackSlice const& slice,
SpecialProperties special,
VPackOptions const* options,
VPackSlice const* base) {
tao::json::value rv;
switch (slice.type()) {
case VPackValueType::Null: {
Expand Down Expand Up @@ -132,16 +170,16 @@ tao::json::value
return rv;
}
case VPackValueType::Array: {
return slice_array_to_value(slice, ignore_special, options, base);
return slice_array_to_value(slice, special, options, base);
return rv;
}
case VPackValueType::Object: {
return slice_object_to_value(slice, ignore_special, options, base);
return slice_object_to_value(slice, special, options, base);
return rv;
}
case VPackValueType::External: {
return slice_to_value(
VPackSlice(reinterpret_cast<uint8_t const*>(slice.getExternal())), ignore_special, options, base);
VPackSlice(reinterpret_cast<uint8_t const*>(slice.getExternal())), special, options, base);
return rv;
}
case VPackValueType::Custom: {
Expand Down
2 changes: 1 addition & 1 deletion 3rdParty/taocpp-json.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
https://github.com/taocpp/json/commit/631095f4bfa45c63af5ccc1cb35ea16eb9ead450
https://github.com/taocpp/json/commit/70aa99fc24d6499c41c221b6a7106df895492b64
4 changes: 2 additions & 2 deletions 3rdParty/taocpp-json/include/tao/json/binary_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ namespace tao
{
[[nodiscard]] inline bool binary_equal( const binary_view lhs, const binary_view rhs ) noexcept
{
return std::equal( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend() );
return std::equal( lhs.begin(), lhs.end(), rhs.begin(), rhs.end() );
}

[[nodiscard]] inline bool binary_less( const binary_view lhs, const binary_view rhs ) noexcept
{
return std::lexicographical_compare( lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend() );
return std::lexicographical_compare( lhs.begin(), lhs.end(), rhs.begin(), rhs.end() );
}

} // namespace internal
Expand Down
Loading

0 comments on commit 7689112

Please sign in to comment.