mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 19:56:00 +00:00
@@ -52,6 +52,16 @@ template <typename Expected>
|
|||||||
if (not value.is_double())
|
if (not value.is_double())
|
||||||
hasError = true;
|
hasError = true;
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<Expected, boost::json::array>)
|
||||||
|
{
|
||||||
|
if (not value.is_array())
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<Expected, boost::json::object>)
|
||||||
|
{
|
||||||
|
if (not value.is_object())
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
else if constexpr (
|
else if constexpr (
|
||||||
std::is_convertible_v<Expected, uint64_t> or
|
std::is_convertible_v<Expected, uint64_t> or
|
||||||
std::is_convertible_v<Expected, int64_t>)
|
std::is_convertible_v<Expected, int64_t>)
|
||||||
@@ -59,11 +69,6 @@ template <typename Expected>
|
|||||||
if (not value.is_int64() && not value.is_uint64())
|
if (not value.is_int64() && not value.is_uint64())
|
||||||
hasError = true;
|
hasError = true;
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<Expected, boost::json::array>)
|
|
||||||
{
|
|
||||||
if (not value.is_array())
|
|
||||||
hasError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return not hasError;
|
return not hasError;
|
||||||
}
|
}
|
||||||
@@ -308,6 +313,70 @@ public:
|
|||||||
verify(boost::json::value const& value, std::string_view key) const;
|
verify(boost::json::value const& value, std::string_view key) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A meta-validator that specifies a list of requirements to run against
|
||||||
|
* when the type matches the template parameter
|
||||||
|
*/
|
||||||
|
template <typename Type>
|
||||||
|
class IfType final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a validator that validates the specs if the type
|
||||||
|
* matches
|
||||||
|
* @param requirements The requirements to validate against
|
||||||
|
*/
|
||||||
|
template <Requirement... Requirements>
|
||||||
|
IfType(Requirements&&... requirements)
|
||||||
|
{
|
||||||
|
validator_ = [... r = std::forward<Requirements>(requirements)](
|
||||||
|
boost::json::value const& j,
|
||||||
|
std::string_view key) -> MaybeError {
|
||||||
|
// clang-format off
|
||||||
|
std::optional<RPC::Status> firstFailure = std::nullopt;
|
||||||
|
|
||||||
|
// the check logic is the same as fieldspec
|
||||||
|
([&j, &key, &firstFailure, req = &r]() {
|
||||||
|
if (firstFailure)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (auto const res = req->verify(j, key); not res)
|
||||||
|
firstFailure = res.error();
|
||||||
|
}(), ...);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
if (firstFailure)
|
||||||
|
return Error{firstFailure.value()};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify that the element is valid
|
||||||
|
* according the stored requirements when type matches
|
||||||
|
*
|
||||||
|
* @param value The JSON value representing the outer object
|
||||||
|
* @param key The key used to retrieve the element from the outer object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
verify(boost::json::value const& value, std::string_view key) const
|
||||||
|
{
|
||||||
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
|
return {}; // ignore. field does not exist, let 'required' fail
|
||||||
|
// instead
|
||||||
|
|
||||||
|
if (not checkType<Type>(value.as_object().at(key.data())))
|
||||||
|
return {}; // ignore if type does not match
|
||||||
|
|
||||||
|
return validator_(value, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<MaybeError(boost::json::value const&, std::string_view)>
|
||||||
|
validator_;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A meta-validator that allows to specify a custom validation function
|
* @brief A meta-validator that allows to specify a custom validation function
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -220,6 +220,44 @@ TEST_F(RPCBaseTest, ArrayAtValidator)
|
|||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.validate(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCBaseTest, IfTypeValidator)
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
auto spec = RpcSpec{
|
||||||
|
{"mix", Required{},
|
||||||
|
Type<std::string,json::object>{},
|
||||||
|
IfType<json::object>{
|
||||||
|
Section{{ "limit", Required{}, Type<uint32_t>{}, Between<uint32_t>{0, 100}}},
|
||||||
|
Section{{ "limit2", Required{}, Type<uint32_t>{}, Between<uint32_t>{0, 100}}}
|
||||||
|
},
|
||||||
|
IfType<std::string>{LedgerHashValidator,}
|
||||||
|
}};
|
||||||
|
// clang-format on
|
||||||
|
// if json object pass
|
||||||
|
auto passingInput =
|
||||||
|
json::parse(R"({ "mix": {"limit": 42, "limit2": 22} })");
|
||||||
|
ASSERT_TRUE(spec.validate(passingInput));
|
||||||
|
// if string pass
|
||||||
|
passingInput = json::parse(
|
||||||
|
R"({ "mix": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })");
|
||||||
|
ASSERT_TRUE(spec.validate(passingInput));
|
||||||
|
|
||||||
|
// if json object fail at first requirement
|
||||||
|
auto failingInput = json::parse(R"({ "mix": {"limit": "not int"} })");
|
||||||
|
ASSERT_FALSE(spec.validate(failingInput));
|
||||||
|
// if json object fail at second requirement
|
||||||
|
failingInput = json::parse(R"({ "mix": {"limit": 22, "limit2": "y"} })");
|
||||||
|
ASSERT_FALSE(spec.validate(failingInput));
|
||||||
|
|
||||||
|
// if string fail
|
||||||
|
failingInput = json::parse(R"({ "mix": "not hash" })");
|
||||||
|
ASSERT_FALSE(spec.validate(failingInput));
|
||||||
|
|
||||||
|
// type check fail
|
||||||
|
failingInput = json::parse(R"({ "mix": 1213 })");
|
||||||
|
ASSERT_FALSE(spec.validate(failingInput));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, CustomValidator)
|
TEST_F(RPCBaseTest, CustomValidator)
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|||||||
Reference in New Issue
Block a user