mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-24 21:55:56 +00:00
@@ -68,6 +68,7 @@ target_sources(clio PRIVATE
|
|||||||
src/rpc/WorkQueue.cpp
|
src/rpc/WorkQueue.cpp
|
||||||
src/rpc/common/Specs.cpp
|
src/rpc/common/Specs.cpp
|
||||||
src/rpc/common/Validators.cpp
|
src/rpc/common/Validators.cpp
|
||||||
|
src/rpc/common/MetaProcessors.cpp
|
||||||
src/rpc/common/impl/APIVersionParser.cpp
|
src/rpc/common/impl/APIVersionParser.cpp
|
||||||
# RPC impl
|
# RPC impl
|
||||||
src/rpc/common/impl/HandlerProvider.cpp
|
src/rpc/common/impl/HandlerProvider.cpp
|
||||||
|
|||||||
@@ -36,11 +36,21 @@ struct RpcSpec;
|
|||||||
*/
|
*/
|
||||||
// clang-format off
|
// clang-format off
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept Requirement = requires(T a) {
|
concept Requirement = requires(T a, boost::json::value lval) {
|
||||||
{ a.verify(boost::json::value{}, std::string{}) } -> std::same_as<MaybeError>;
|
{ a.verify(lval, std::string{}) } -> std::same_as<MaybeError>;
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
template <typename T>
|
||||||
|
concept Modifier = requires(T a, boost::json::value lval) {
|
||||||
|
{ a.modify(lval, std::string{}) } -> std::same_as<MaybeError>;
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Processor = (Requirement<T> or Modifier<T>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A concept that specifies what a Handler type must provide
|
* @brief A concept that specifies what a Handler type must provide
|
||||||
*
|
*
|
||||||
|
|||||||
71
src/rpc/common/MetaProcessors.cpp
Normal file
71
src/rpc/common/MetaProcessors.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
|
|
||||||
|
#include <boost/json/value.hpp>
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace RPC::meta {
|
||||||
|
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
Section::verify(boost::json::value& 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
|
||||||
|
|
||||||
|
auto& res = value.as_object().at(key.data());
|
||||||
|
|
||||||
|
// if it is not a json object, let other validators fail
|
||||||
|
if (!res.is_object())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
for (auto const& spec : specs)
|
||||||
|
{
|
||||||
|
if (auto const ret = spec.process(res); not ret)
|
||||||
|
return Error{ret.error()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
ValidateArrayAt::verify(boost::json::value& 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 value.as_object().at(key.data()).is_array())
|
||||||
|
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||||
|
|
||||||
|
auto& arr = value.as_object().at(key.data()).as_array();
|
||||||
|
if (idx_ >= arr.size())
|
||||||
|
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||||
|
|
||||||
|
auto& res = arr.at(idx_);
|
||||||
|
for (auto const& spec : specs_)
|
||||||
|
if (auto const ret = spec.process(res); not ret)
|
||||||
|
return Error{ret.error()};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace RPC::meta
|
||||||
175
src/rpc/common/MetaProcessors.h
Normal file
175
src/rpc/common/MetaProcessors.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <rpc/common/Concepts.h>
|
||||||
|
#include <rpc/common/Specs.h>
|
||||||
|
#include <rpc/common/Types.h>
|
||||||
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
|
||||||
|
namespace RPC::meta {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A meta-processor that acts as a spec for a sub-object/section
|
||||||
|
*/
|
||||||
|
class Section final
|
||||||
|
{
|
||||||
|
std::vector<FieldSpec> specs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct new section validator from a list of specs
|
||||||
|
*
|
||||||
|
* @param specs List of specs @ref FieldSpec
|
||||||
|
*/
|
||||||
|
explicit Section(std::initializer_list<FieldSpec> specs) : specs{specs}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify that the JSON value representing the section is valid according to the given specs
|
||||||
|
*
|
||||||
|
* @param value The JSON value representing the outer object
|
||||||
|
* @param key The key used to retrieve the section from the outer object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
verify(boost::json::value& value, std::string_view key) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A meta-processor that specifies a list of specs to run against the object at the given index in the array
|
||||||
|
*/
|
||||||
|
class ValidateArrayAt final
|
||||||
|
{
|
||||||
|
std::size_t idx_;
|
||||||
|
std::vector<FieldSpec> specs_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a processor that validates the specified element of a JSON array
|
||||||
|
*
|
||||||
|
* @param idx The index inside the array to validate
|
||||||
|
* @param specs The specifications to validate against
|
||||||
|
*/
|
||||||
|
ValidateArrayAt(std::size_t idx, std::initializer_list<FieldSpec> specs) : idx_{idx}, specs_{specs}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify that the JSON array element at given index is valid according the stored specs
|
||||||
|
*
|
||||||
|
* @param value The JSON value representing the outer object
|
||||||
|
* @param key The key used to retrieve the array from the outer object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
verify(boost::json::value& value, std::string_view key) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A meta-processor 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)
|
||||||
|
{
|
||||||
|
processor_ = [... r = std::forward<Requirements>(requirements)](
|
||||||
|
boost::json::value& j, std::string_view key) -> MaybeError {
|
||||||
|
std::optional<Status> firstFailure = std::nullopt;
|
||||||
|
|
||||||
|
// the check logic is the same as fieldspec
|
||||||
|
// clang-format off
|
||||||
|
([&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& 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 RPC::validation::checkType<Type>(value.as_object().at(key.data())))
|
||||||
|
return {}; // ignore if type does not match
|
||||||
|
|
||||||
|
return processor_(value, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<MaybeError(boost::json::value&, std::string_view)> processor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A meta-processor that wraps another validator and produces a custom error in case the wrapped validator fails
|
||||||
|
*/
|
||||||
|
template <typename Requirement>
|
||||||
|
class WithCustomError final
|
||||||
|
{
|
||||||
|
Requirement requirement;
|
||||||
|
Status error;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructs a validator that calls the given validator `req` and returns a custom error `err` in case `req`
|
||||||
|
* fails
|
||||||
|
*/
|
||||||
|
WithCustomError(Requirement req, Status err) : requirement{std::move(req)}, error{err}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
verify(boost::json::value const& value, std::string_view key) const
|
||||||
|
{
|
||||||
|
if (auto const res = requirement.verify(value, key); not res)
|
||||||
|
return Error{error};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace RPC::meta
|
||||||
70
src/rpc/common/Modifiers.h
Normal file
70
src/rpc/common/Modifiers.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <rpc/common/Concepts.h>
|
||||||
|
#include <rpc/common/Specs.h>
|
||||||
|
#include <rpc/common/Types.h>
|
||||||
|
|
||||||
|
namespace RPC::modifiers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clamp value between min and max.
|
||||||
|
*/
|
||||||
|
template <typename Type>
|
||||||
|
class Clamp final
|
||||||
|
{
|
||||||
|
Type min_;
|
||||||
|
Type max_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct the modifier storing min and max values
|
||||||
|
*
|
||||||
|
* @param min
|
||||||
|
* @param max
|
||||||
|
*/
|
||||||
|
explicit Clamp(Type min, Type max) : min_{min}, max_{max}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clamp the value to stored min and max values.
|
||||||
|
*
|
||||||
|
* @param value The JSON value representing the outer object
|
||||||
|
* @param key The key used to retrieve the modified value from the outer object
|
||||||
|
*/
|
||||||
|
[[nodiscard]] MaybeError
|
||||||
|
modify(boost::json::value& value, std::string_view key) const
|
||||||
|
{
|
||||||
|
using boost::json::value_to;
|
||||||
|
|
||||||
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
|
|
||||||
|
// clamp to min_ and max_
|
||||||
|
auto const oldValue = value_to<Type>(value.as_object().at(key.data()));
|
||||||
|
value.as_object()[key.data()] = std::clamp<Type>(oldValue, min_, max_);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace RPC::modifiers
|
||||||
@@ -24,16 +24,16 @@
|
|||||||
namespace RPC {
|
namespace RPC {
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
FieldSpec::validate(boost::json::value const& value) const
|
FieldSpec::process(boost::json::value& value) const
|
||||||
{
|
{
|
||||||
return validator_(value);
|
return processor_(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
RpcSpec::validate(boost::json::value const& value) const
|
RpcSpec::process(boost::json::value& value) const
|
||||||
{
|
{
|
||||||
for (auto const& field : fields_)
|
for (auto const& field : fields_)
|
||||||
if (auto ret = field.validate(value); not ret)
|
if (auto ret = field.process(value); not ret)
|
||||||
return Error{ret.error()};
|
return Error{ret.error()};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -34,30 +34,29 @@ namespace RPC {
|
|||||||
struct FieldSpec final
|
struct FieldSpec final
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief Construct a field specification out of a set of requirements
|
* @brief Construct a field specification out of a set of processors
|
||||||
*
|
*
|
||||||
* @tparam Requirements The types of requirements @ref Requirement
|
* @tparam Processors The types of processors @ref Processor
|
||||||
* @param key The key in a JSON object that the field validates
|
* @param key The key in a JSON object that the field validates
|
||||||
* @param requirements The requirements, each of them have to fulfil
|
* @param processors The processors, each of them have to fulfil the @ref Processor concept
|
||||||
* the @ref Requirement concept
|
|
||||||
*/
|
*/
|
||||||
template <Requirement... Requirements>
|
template <Processor... Processors>
|
||||||
FieldSpec(std::string const& key, Requirements&&... requirements)
|
FieldSpec(std::string const& key, Processors&&... processors)
|
||||||
: validator_{detail::makeFieldValidator<Requirements...>(key, std::forward<Requirements>(requirements)...)}
|
: processor_{detail::makeFieldProcessor<Processors...>(key, std::forward<Processors>(processors)...)}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validates the passed JSON value using the stored requirements
|
* @brief Processos the passed JSON value using the stored processors
|
||||||
*
|
*
|
||||||
* @param value The JSON value to validate
|
* @param value The JSON value to validate and/or modify
|
||||||
* @return Nothing on success; @ref Status on error
|
* @return Nothing on success; @ref Status on error
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
validate(boost::json::value const& value) const;
|
process(boost::json::value& value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<MaybeError(boost::json::value const&)> validator_;
|
std::function<MaybeError(boost::json::value&)> processor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,13 +77,13 @@ struct RpcSpec final
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Validates the passed JSON value using the stored field specs
|
* @brief Processos the passed JSON value using the stored field specs
|
||||||
*
|
*
|
||||||
* @param value The JSON value to validate
|
* @param value The JSON value to validate and/or modify
|
||||||
* @return Nothing on success; @ref Status on error
|
* @return Nothing on success; @ref Status on error
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
validate(boost::json::value const& value) const;
|
process(boost::json::value& value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<FieldSpec> fields_;
|
std::vector<FieldSpec> fields_;
|
||||||
|
|||||||
@@ -28,28 +28,6 @@
|
|||||||
|
|
||||||
namespace RPC::validation {
|
namespace RPC::validation {
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
|
||||||
Section::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
|
|
||||||
|
|
||||||
auto const& res = value.at(key.data());
|
|
||||||
|
|
||||||
// if it is not a json object, let other validators fail
|
|
||||||
if (!res.is_object())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
for (auto const& spec : specs)
|
|
||||||
{
|
|
||||||
if (auto const ret = spec.validate(res); not ret)
|
|
||||||
return Error{ret.error()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
Required::verify(boost::json::value const& value, std::string_view key) const
|
Required::verify(boost::json::value const& value, std::string_view key) const
|
||||||
{
|
{
|
||||||
@@ -59,34 +37,11 @@ Required::verify(boost::json::value const& value, std::string_view key) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
|
||||||
ValidateArrayAt::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 value.as_object().at(key.data()).is_array())
|
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
|
||||||
|
|
||||||
auto const& arr = value.as_object().at(key.data()).as_array();
|
|
||||||
if (idx_ >= arr.size())
|
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
|
||||||
|
|
||||||
auto const& res = arr.at(idx_);
|
|
||||||
for (auto const& spec : specs_)
|
|
||||||
if (auto const ret = spec.validate(res); not ret)
|
|
||||||
return Error{ret.error()};
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
[[nodiscard]] MaybeError
|
||||||
CustomValidator::verify(boost::json::value const& value, std::string_view key) const
|
CustomValidator::verify(boost::json::value const& value, std::string_view key) const
|
||||||
{
|
{
|
||||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
return {}; // ignore. field does not exist, let 'required' fail
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
// instead
|
|
||||||
|
|
||||||
return validator_(value.as_object().at(key.data()), key);
|
return validator_(value.as_object().at(key.data()), key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,34 +72,6 @@ template <typename Expected>
|
|||||||
return not hasError;
|
return not hasError;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A meta-validator that acts as a spec for a sub-object/section
|
|
||||||
*/
|
|
||||||
class Section final
|
|
||||||
{
|
|
||||||
std::vector<FieldSpec> specs;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct new section validator from a list of specs
|
|
||||||
*
|
|
||||||
* @param specs List of specs @ref FieldSpec
|
|
||||||
*/
|
|
||||||
explicit Section(std::initializer_list<FieldSpec> specs) : specs{specs}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Verify that the JSON value representing the section is valid
|
|
||||||
* according to the given specs
|
|
||||||
*
|
|
||||||
* @param value The JSON value representing the outer object
|
|
||||||
* @param key The key used to retrieve the section from the outer object
|
|
||||||
*/
|
|
||||||
[[nodiscard]] MaybeError
|
|
||||||
verify(boost::json::value const& value, std::string_view key) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A validator that simply requires a field to be present
|
* @brief A validator that simply requires a field to be present
|
||||||
*/
|
*/
|
||||||
@@ -184,8 +156,7 @@ struct Type final
|
|||||||
verify(boost::json::value const& value, std::string_view key) const
|
verify(boost::json::value const& value, std::string_view key) const
|
||||||
{
|
{
|
||||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
return {}; // ignore. field does not exist, let 'required' fail
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
// instead
|
|
||||||
|
|
||||||
auto const& res = value.as_object().at(key.data());
|
auto const& res = value.as_object().at(key.data());
|
||||||
auto const convertible = (checkType<Types>(res) || ...);
|
auto const convertible = (checkType<Types>(res) || ...);
|
||||||
@@ -230,8 +201,7 @@ public:
|
|||||||
using boost::json::value_to;
|
using boost::json::value_to;
|
||||||
|
|
||||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
return {}; // ignore. field does not exist, let 'required' fail
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
// instead
|
|
||||||
|
|
||||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||||
|
|
||||||
@@ -275,8 +245,7 @@ public:
|
|||||||
using boost::json::value_to;
|
using boost::json::value_to;
|
||||||
|
|
||||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
return {}; // ignore. field does not exist, let 'required' fail
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
// instead
|
|
||||||
|
|
||||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||||
if (res != original_)
|
if (res != original_)
|
||||||
@@ -333,8 +302,7 @@ public:
|
|||||||
using boost::json::value_to;
|
using boost::json::value_to;
|
||||||
|
|
||||||
if (not value.is_object() or not value.as_object().contains(key.data()))
|
if (not value.is_object() or not value.as_object().contains(key.data()))
|
||||||
return {}; // ignore. field does not exist, let 'required' fail
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||||
// instead
|
|
||||||
|
|
||||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||||
if (std::find(std::begin(options_), std::end(options_), res) == std::end(options_))
|
if (std::find(std::begin(options_), std::end(options_), res) == std::end(options_))
|
||||||
@@ -350,129 +318,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
OneOf(std::initializer_list<char const*>)->OneOf<std::string>;
|
OneOf(std::initializer_list<char const*>)->OneOf<std::string>;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A meta-validator that specifies a list of specs to run against the
|
|
||||||
* object at the given index in the array
|
|
||||||
*/
|
|
||||||
class ValidateArrayAt final
|
|
||||||
{
|
|
||||||
std::size_t idx_;
|
|
||||||
std::vector<FieldSpec> specs_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructs a validator that validates the specified element of a
|
|
||||||
* JSON array
|
|
||||||
*
|
|
||||||
* @param idx The index inside the array to validate
|
|
||||||
* @param specs The specifications to validate against
|
|
||||||
*/
|
|
||||||
ValidateArrayAt(std::size_t idx, std::initializer_list<FieldSpec> specs) : idx_{idx}, specs_{specs}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Verify that the JSON array element at given index is valid
|
|
||||||
* according the stored specs
|
|
||||||
*
|
|
||||||
* @param value The JSON value representing the outer object
|
|
||||||
* @param key The key used to retrieve the array from the outer object
|
|
||||||
*/
|
|
||||||
[[nodiscard]] MaybeError
|
|
||||||
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 {
|
|
||||||
std::optional<Status> firstFailure = std::nullopt;
|
|
||||||
|
|
||||||
// the check logic is the same as fieldspec
|
|
||||||
// clang-format off
|
|
||||||
([&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 wrapp other validator to send the customized
|
|
||||||
* error
|
|
||||||
*/
|
|
||||||
template <typename Requirement>
|
|
||||||
class WithCustomError final
|
|
||||||
{
|
|
||||||
Requirement requirement;
|
|
||||||
Status error;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructs a validator that calls the given validator "req" and
|
|
||||||
* return customized error "err"
|
|
||||||
*/
|
|
||||||
WithCustomError(Requirement req, Status err) : requirement{std::move(req)}, error{err}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] MaybeError
|
|
||||||
verify(boost::json::value const& value, std::string_view key) const
|
|
||||||
{
|
|
||||||
if (auto const res = requirement.verify(value, key); not res)
|
|
||||||
return Error{error};
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A meta-validator that allows to specify a custom validation function
|
* @brief A meta-validator that allows to specify a custom validation function
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -28,23 +28,33 @@
|
|||||||
|
|
||||||
namespace RPC::detail {
|
namespace RPC::detail {
|
||||||
|
|
||||||
template <Requirement... Requirements>
|
template <typename>
|
||||||
|
static constexpr bool unsupported_v = false;
|
||||||
|
|
||||||
|
template <Processor... Processors>
|
||||||
[[nodiscard]] auto
|
[[nodiscard]] auto
|
||||||
makeFieldValidator(std::string const& key, Requirements&&... requirements)
|
makeFieldProcessor(std::string const& key, Processors&&... procs)
|
||||||
{
|
{
|
||||||
return [key, ... r = std::forward<Requirements>(requirements)](boost::json::value const& j) -> MaybeError {
|
return [key, ... proc = std::forward<Processors>(procs)](boost::json::value& j) -> MaybeError {
|
||||||
std::optional<Status> firstFailure = std::nullopt;
|
std::optional<Status> firstFailure = std::nullopt;
|
||||||
|
|
||||||
// This expands in order of Requirements and stops evaluating after
|
// This expands in order of Requirements and stops evaluating after first failure which is stored in
|
||||||
// first failure which is stored in `firstFailure` and can be checked
|
// `firstFailure` and can be checked later on to see whether the verification failed as a whole or not.
|
||||||
// later on to see whether the verification failed as a whole or not.
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
([&j, &key, &firstFailure, req = &r]() {
|
([&j, &key, &firstFailure, req = &proc]() {
|
||||||
if (firstFailure)
|
if (firstFailure)
|
||||||
return; // already failed earlier - skip
|
return; // already failed earlier - skip
|
||||||
|
|
||||||
if (auto const res = req->verify(j, key); not res)
|
if constexpr (Requirement<decltype(*req)>) {
|
||||||
firstFailure = res.error();
|
if (auto const res = req->verify(j, key); not res)
|
||||||
|
firstFailure = res.error();
|
||||||
|
} else if constexpr (Modifier<decltype(*req)>) {
|
||||||
|
if (auto const res = req->modify(j, key); not res)
|
||||||
|
firstFailure = res.error();
|
||||||
|
} else {
|
||||||
|
static_assert(unsupported_v<decltype(*req)>);
|
||||||
|
}
|
||||||
|
|
||||||
}(), ...);
|
}(), ...);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,12 @@ struct DefaultProcessor final
|
|||||||
{
|
{
|
||||||
// first we run validation against specified API version
|
// first we run validation against specified API version
|
||||||
auto const spec = handler.spec(ctx.apiVersion);
|
auto const spec = handler.spec(ctx.apiVersion);
|
||||||
if (auto const ret = spec.validate(value); not ret)
|
auto input = value; // copy here, spec require mutable data
|
||||||
|
|
||||||
|
if (auto const ret = spec.process(input); not ret)
|
||||||
return Error{ret.error()}; // forward Status
|
return Error{ret.error()}; // forward Status
|
||||||
|
|
||||||
auto const inData = value_to<typename HandlerType::Input>(value);
|
auto const inData = value_to<typename HandlerType::Input>(input);
|
||||||
auto const ret = handler.process(inData, ctx);
|
auto const ret = handler.process(inData, ctx);
|
||||||
|
|
||||||
// real handler is given expected Input, not json
|
// real handler is given expected Input, not json
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ public:
|
|||||||
{JS(account), validation::Required{}, validation::AccountValidator},
|
{JS(account), validation::Required{}, validation::AccountValidator},
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
{JS(strict), validation::IfType<bool>{validation::NotSupported{false}}},
|
{JS(strict), meta::IfType<bool>{validation::NotSupported{false}}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ public:
|
|||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
{JS(signer_lists), validation::Type<bool>{}},
|
{JS(signer_lists), validation::Type<bool>{}},
|
||||||
{JS(strict), validation::IfType<bool>{validation::NotSupported{false}}},
|
{JS(strict), meta::IfType<bool>{validation::NotSupported{false}}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -92,9 +93,8 @@ public:
|
|||||||
static auto const rpcSpec = RpcSpec{
|
static auto const rpcSpec = RpcSpec{
|
||||||
{JS(account),
|
{JS(account),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
|
meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
|
||||||
{JS(peer),
|
{JS(peer), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
|
||||||
validation::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}},
|
|
||||||
{JS(ignore_default), validation::Type<bool>{}},
|
{JS(ignore_default), validation::Type<bool>{}},
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(limit), validation::Type<uint32_t>{}, validation::Between{10, 400}},
|
{JS(limit), validation::Type<uint32_t>{}, validation::Between{10, 400}},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ public:
|
|||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
{JS(marker), validation::AccountMarkerValidator},
|
{JS(marker), validation::AccountMarkerValidator},
|
||||||
{JS(limit), validation::Type<uint32_t>{}, validation::Between{10, 400}},
|
{JS(limit), validation::Type<uint32_t>{}, validation::Between{10, 400}},
|
||||||
{JS(strict), validation::IfType<bool>{validation::NotSupported{false}}},
|
{JS(strict), meta::IfType<bool>{validation::NotSupported{false}}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -92,11 +93,11 @@ public:
|
|||||||
{JS(forward), validation::Type<bool>{}},
|
{JS(forward), validation::Type<bool>{}},
|
||||||
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, std::numeric_limits<int32_t>::max()}},
|
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, std::numeric_limits<int32_t>::max()}},
|
||||||
{JS(marker),
|
{JS(marker),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::Type<boost::json::object>{},
|
validation::Type<boost::json::object>{},
|
||||||
Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"},
|
Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"},
|
||||||
},
|
},
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
|
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
|
||||||
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
|
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
|
||||||
}},
|
}},
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -71,28 +72,24 @@ public:
|
|||||||
{JS(taker_gets),
|
{JS(taker_gets),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::Type<boost::json::object>{},
|
validation::Type<boost::json::object>{},
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(currency),
|
{JS(currency),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcDST_AMT_MALFORMED)}},
|
||||||
validation::CurrencyValidator, Status(RippledError::rpcDST_AMT_MALFORMED)}},
|
|
||||||
{JS(issuer),
|
{JS(issuer),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::IssuerValidator, Status(RippledError::rpcDST_ISR_MALFORMED)}}}},
|
||||||
validation::IssuerValidator, Status(RippledError::rpcDST_ISR_MALFORMED)}}}},
|
|
||||||
{JS(taker_pays),
|
{JS(taker_pays),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::Type<boost::json::object>{},
|
validation::Type<boost::json::object>{},
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(currency),
|
{JS(currency),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::CurrencyValidator, Status(RippledError::rpcSRC_CUR_MALFORMED)}},
|
||||||
validation::CurrencyValidator, Status(RippledError::rpcSRC_CUR_MALFORMED)}},
|
|
||||||
{JS(issuer),
|
{JS(issuer),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::IssuerValidator, Status(RippledError::rpcSRC_ISR_MALFORMED)}}}},
|
||||||
validation::IssuerValidator, Status(RippledError::rpcSRC_ISR_MALFORMED)}}}},
|
|
||||||
// return INVALID_PARAMS if account format is wrong for "taker"
|
// return INVALID_PARAMS if account format is wrong for "taker"
|
||||||
{JS(taker),
|
{JS(taker),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::AccountValidator, Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'")}},
|
validation::AccountValidator, Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'")}},
|
||||||
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, 100}},
|
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, 100}},
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ public:
|
|||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
{JS(ledger_index), validation::LedgerIndexValidator},
|
{JS(ledger_index), validation::LedgerIndexValidator},
|
||||||
{JS(hotwallet), hotWalletValidator},
|
{JS(hotwallet), hotWalletValidator},
|
||||||
{JS(strict), validation::IfType<bool>{validation::NotSupported{false}}},
|
{JS(strict), meta::IfType<bool>{validation::NotSupported{false}}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -94,12 +95,12 @@ public:
|
|||||||
{JS(limit), validation::Type<uint32_t>{}},
|
{JS(limit), validation::Type<uint32_t>{}},
|
||||||
{JS(marker),
|
{JS(marker),
|
||||||
validation::Type<uint32_t, std::string>{},
|
validation::Type<uint32_t, std::string>{},
|
||||||
validation::IfType<std::string>{validation::Uint256HexStringValidator}},
|
meta::IfType<std::string>{validation::Uint256HexStringValidator}},
|
||||||
{JS(type),
|
{JS(type),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::Type<std::string>{},
|
validation::Type<std::string>{},
|
||||||
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type', not string."}},
|
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type', not string."}},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend()),
|
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend()),
|
||||||
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type'."}}},
|
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type'."}}},
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -98,10 +99,10 @@ public:
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
static auto const malformedRequestHexStringValidator =
|
static auto const malformedRequestHexStringValidator =
|
||||||
validation::WithCustomError{validation::Uint256HexStringValidator, Status(ClioError::rpcMALFORMED_REQUEST)};
|
meta::WithCustomError{validation::Uint256HexStringValidator, Status(ClioError::rpcMALFORMED_REQUEST)};
|
||||||
|
|
||||||
static auto const malformedRequestIntValidator =
|
static auto const malformedRequestIntValidator =
|
||||||
validation::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::rpcMALFORMED_REQUEST)};
|
meta::WithCustomError{validation::Type<uint32_t>{}, Status(ClioError::rpcMALFORMED_REQUEST)};
|
||||||
|
|
||||||
static auto const rpcSpec = RpcSpec{
|
static auto const rpcSpec = RpcSpec{
|
||||||
{JS(binary), validation::Type<bool>{}},
|
{JS(binary), validation::Type<bool>{}},
|
||||||
@@ -112,40 +113,38 @@ public:
|
|||||||
{JS(check), malformedRequestHexStringValidator},
|
{JS(check), malformedRequestHexStringValidator},
|
||||||
{JS(deposit_preauth),
|
{JS(deposit_preauth),
|
||||||
validation::Type<std::string, boost::json::object>{},
|
validation::Type<std::string, boost::json::object>{},
|
||||||
validation::IfType<std::string>{malformedRequestHexStringValidator},
|
meta::IfType<std::string>{malformedRequestHexStringValidator},
|
||||||
validation::IfType<boost::json::object>{
|
meta::IfType<boost::json::object>{
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(owner),
|
{JS(owner),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_OWNER)}},
|
||||||
validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_OWNER)}},
|
|
||||||
{JS(authorized), validation::Required{}, validation::AccountBase58Validator},
|
{JS(authorized), validation::Required{}, validation::AccountBase58Validator},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
{JS(directory),
|
{JS(directory),
|
||||||
validation::Type<std::string, boost::json::object>{},
|
validation::Type<std::string, boost::json::object>{},
|
||||||
validation::IfType<std::string>{malformedRequestHexStringValidator},
|
meta::IfType<std::string>{malformedRequestHexStringValidator},
|
||||||
validation::IfType<boost::json::object>{validation::Section{
|
meta::IfType<boost::json::object>{meta::Section{
|
||||||
{JS(owner), validation::AccountBase58Validator},
|
{JS(owner), validation::AccountBase58Validator},
|
||||||
{JS(dir_root), validation::Uint256HexStringValidator},
|
{JS(dir_root), validation::Uint256HexStringValidator},
|
||||||
{JS(sub_index), malformedRequestIntValidator}}}},
|
{JS(sub_index), malformedRequestIntValidator}}}},
|
||||||
{JS(escrow),
|
{JS(escrow),
|
||||||
validation::Type<std::string, boost::json::object>{},
|
validation::Type<std::string, boost::json::object>{},
|
||||||
validation::IfType<std::string>{malformedRequestHexStringValidator},
|
meta::IfType<std::string>{malformedRequestHexStringValidator},
|
||||||
validation::IfType<boost::json::object>{
|
meta::IfType<boost::json::object>{
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(owner),
|
{JS(owner),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_OWNER)}},
|
||||||
validation::AccountBase58Validator, Status(ClioError::rpcMALFORMED_OWNER)}},
|
|
||||||
{JS(seq), validation::Required{}, malformedRequestIntValidator},
|
{JS(seq), validation::Required{}, malformedRequestIntValidator},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
{JS(offer),
|
{JS(offer),
|
||||||
validation::Type<std::string, boost::json::object>{},
|
validation::Type<std::string, boost::json::object>{},
|
||||||
validation::IfType<std::string>{malformedRequestHexStringValidator},
|
meta::IfType<std::string>{malformedRequestHexStringValidator},
|
||||||
validation::IfType<boost::json::object>{
|
meta::IfType<boost::json::object>{
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(account), validation::Required{}, validation::AccountBase58Validator},
|
{JS(account), validation::Required{}, validation::AccountBase58Validator},
|
||||||
{JS(seq), validation::Required{}, malformedRequestIntValidator},
|
{JS(seq), validation::Required{}, malformedRequestIntValidator},
|
||||||
},
|
},
|
||||||
@@ -153,15 +152,15 @@ public:
|
|||||||
{JS(payment_channel), malformedRequestHexStringValidator},
|
{JS(payment_channel), malformedRequestHexStringValidator},
|
||||||
{JS(ripple_state),
|
{JS(ripple_state),
|
||||||
validation::Type<boost::json::object>{},
|
validation::Type<boost::json::object>{},
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(accounts), validation::Required{}, rippleStateAccountsCheck},
|
{JS(accounts), validation::Required{}, rippleStateAccountsCheck},
|
||||||
{JS(currency), validation::Required{}, validation::CurrencyValidator},
|
{JS(currency), validation::Required{}, validation::CurrencyValidator},
|
||||||
}},
|
}},
|
||||||
{JS(ticket),
|
{JS(ticket),
|
||||||
validation::Type<std::string, boost::json::object>{},
|
validation::Type<std::string, boost::json::object>{},
|
||||||
validation::IfType<std::string>{malformedRequestHexStringValidator},
|
meta::IfType<std::string>{malformedRequestHexStringValidator},
|
||||||
validation::IfType<boost::json::object>{
|
meta::IfType<boost::json::object>{
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(account), validation::Required{}, validation::AccountBase58Validator},
|
{JS(account), validation::Required{}, validation::AccountBase58Validator},
|
||||||
{JS(ticket_seq), validation::Required{}, malformedRequestIntValidator},
|
{JS(ticket_seq), validation::Required{}, malformedRequestIntValidator},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -92,9 +93,9 @@ public:
|
|||||||
{JS(forward), validation::Type<bool>{}},
|
{JS(forward), validation::Type<bool>{}},
|
||||||
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, 100}},
|
{JS(limit), validation::Type<uint32_t>{}, validation::Between{1, 100}},
|
||||||
{JS(marker),
|
{JS(marker),
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::Type<boost::json::object>{}, Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"}},
|
validation::Type<boost::json::object>{}, Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"}},
|
||||||
validation::Section{
|
meta::Section{
|
||||||
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
|
{JS(ledger), validation::Required{}, validation::Type<uint32_t>{}},
|
||||||
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
|
{JS(seq), validation::Required{}, validation::Type<uint32_t>{}},
|
||||||
}},
|
}},
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ public:
|
|||||||
{JS(account), validation::Required{}, validation::AccountValidator},
|
{JS(account), validation::Required{}, validation::AccountValidator},
|
||||||
{JS(role),
|
{JS(role),
|
||||||
validation::Required{},
|
validation::Required{},
|
||||||
validation::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::OneOf{"gateway", "user"},
|
validation::OneOf{"gateway", "user"},
|
||||||
Status{RippledError::rpcINVALID_PARAMS, "role field is invalid"}}},
|
Status{RippledError::rpcINVALID_PARAMS, "role field is invalid"}}},
|
||||||
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
{JS(ledger_hash), validation::Uint256HexStringValidator},
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include <rpc/Factories.h>
|
#include <rpc/Factories.h>
|
||||||
#include <rpc/common/AnyHandler.h>
|
#include <rpc/common/AnyHandler.h>
|
||||||
|
#include <rpc/common/MetaProcessors.h>
|
||||||
|
#include <rpc/common/Modifiers.h>
|
||||||
#include <rpc/common/Specs.h>
|
#include <rpc/common/Specs.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
@@ -36,6 +38,8 @@ using namespace std;
|
|||||||
|
|
||||||
using namespace RPC;
|
using namespace RPC;
|
||||||
using namespace RPC::validation;
|
using namespace RPC::validation;
|
||||||
|
using namespace RPC::meta;
|
||||||
|
using namespace RPC::modifiers;
|
||||||
|
|
||||||
namespace json = boost::json;
|
namespace json = boost::json;
|
||||||
|
|
||||||
@@ -92,31 +96,31 @@ TEST_F(RPCBaseTest, TypeValidator)
|
|||||||
"bool": true,
|
"bool": true,
|
||||||
"arr": []
|
"arr": []
|
||||||
})");
|
})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "uint": "a string" })");
|
auto failingInput = json::parse(R"({ "uint": "a string" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "int": "a string" })");
|
auto failingInput = json::parse(R"({ "int": "a string" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "str": 1234 })");
|
auto failingInput = json::parse(R"({ "str": 1234 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "double": "a string" })");
|
auto failingInput = json::parse(R"({ "double": "a string" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "bool": "a string" })");
|
auto failingInput = json::parse(R"({ "bool": "a string" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto failingInput = json::parse(R"({ "arr": "a string" })");
|
auto failingInput = json::parse(R"({ "arr": "a string" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,13 +132,13 @@ TEST_F(RPCBaseTest, TypeValidatorMultipleTypes)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "test": "1234" })");
|
auto passingInput = json::parse(R"({ "test": "1234" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto passingInput2 = json::parse(R"({ "test": 1234 })");
|
auto passingInput2 = json::parse(R"({ "test": 1234 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput2));
|
ASSERT_TRUE(spec.process(passingInput2));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "test": true })");
|
auto failingInput = json::parse(R"({ "test": true })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, RequiredValidator)
|
TEST_F(RPCBaseTest, RequiredValidator)
|
||||||
@@ -144,13 +148,13 @@ TEST_F(RPCBaseTest, RequiredValidator)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "required": "present" })");
|
auto passingInput = json::parse(R"({ "required": "present" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto passingInput2 = json::parse(R"({ "required": true })");
|
auto passingInput2 = json::parse(R"({ "required": true })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput2));
|
ASSERT_TRUE(spec.process(passingInput2));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({})");
|
auto failingInput = json::parse(R"({})");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, BetweenValidator)
|
TEST_F(RPCBaseTest, BetweenValidator)
|
||||||
@@ -160,19 +164,19 @@ TEST_F(RPCBaseTest, BetweenValidator)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "amount": 15 })");
|
auto passingInput = json::parse(R"({ "amount": 15 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto passingInput2 = json::parse(R"({ "amount": 10 })");
|
auto passingInput2 = json::parse(R"({ "amount": 10 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput2));
|
ASSERT_TRUE(spec.process(passingInput2));
|
||||||
|
|
||||||
auto passingInput3 = json::parse(R"({ "amount": 20 })");
|
auto passingInput3 = json::parse(R"({ "amount": 20 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput3));
|
ASSERT_TRUE(spec.process(passingInput3));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "amount": 9 })");
|
auto failingInput = json::parse(R"({ "amount": 9 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
auto failingInput2 = json::parse(R"({ "amount": 21 })");
|
auto failingInput2 = json::parse(R"({ "amount": 21 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput2));
|
ASSERT_FALSE(spec.process(failingInput2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, OneOfValidator)
|
TEST_F(RPCBaseTest, OneOfValidator)
|
||||||
@@ -182,13 +186,13 @@ TEST_F(RPCBaseTest, OneOfValidator)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "currency": "XRP" })");
|
auto passingInput = json::parse(R"({ "currency": "XRP" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto passingInput2 = json::parse(R"({ "currency": "USD" })");
|
auto passingInput2 = json::parse(R"({ "currency": "USD" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput2));
|
ASSERT_TRUE(spec.process(passingInput2));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "currency": "PRX" })");
|
auto failingInput = json::parse(R"({ "currency": "PRX" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, EqualToValidator)
|
TEST_F(RPCBaseTest, EqualToValidator)
|
||||||
@@ -198,10 +202,10 @@ TEST_F(RPCBaseTest, EqualToValidator)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "exact": "CaseSensitive" })");
|
auto passingInput = json::parse(R"({ "exact": "CaseSensitive" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "exact": "Different" })");
|
auto failingInput = json::parse(R"({ "exact": "Different" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, ArrayAtValidator)
|
TEST_F(RPCBaseTest, ArrayAtValidator)
|
||||||
@@ -218,16 +222,16 @@ TEST_F(RPCBaseTest, ArrayAtValidator)
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "arr": [{"limit": 42}] })");
|
auto passingInput = json::parse(R"({ "arr": [{"limit": 42}] })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "arr": [{"limit": "not int"}] })");
|
auto failingInput = json::parse(R"({ "arr": [{"limit": "not int"}] })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "arr": [{"limit": 42}] ,"arr2": "not array type" })");
|
failingInput = json::parse(R"({ "arr": [{"limit": 42}] ,"arr2": "not array type" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "arr": [] })");
|
failingInput = json::parse(R"({ "arr": [] })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, IfTypeValidator)
|
TEST_F(RPCBaseTest, IfTypeValidator)
|
||||||
@@ -253,28 +257,28 @@ TEST_F(RPCBaseTest, IfTypeValidator)
|
|||||||
|
|
||||||
// if json object pass
|
// if json object pass
|
||||||
auto passingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} })");
|
auto passingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
// if string pass
|
// if string pass
|
||||||
passingInput = json::parse(R"({ "mix": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })");
|
passingInput = json::parse(R"({ "mix": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
// if json object fail at first requirement
|
// if json object fail at first requirement
|
||||||
auto failingInput = json::parse(R"({ "mix": {"limit": "not int"} })");
|
auto failingInput = json::parse(R"({ "mix": {"limit": "not int"} })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
// if json object fail at second requirement
|
// if json object fail at second requirement
|
||||||
failingInput = json::parse(R"({ "mix": {"limit": 22, "limit2": "y"} })");
|
failingInput = json::parse(R"({ "mix": {"limit": 22, "limit2": "y"} })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
// if string fail
|
// if string fail
|
||||||
failingInput = json::parse(R"({ "mix": "not hash" })");
|
failingInput = json::parse(R"({ "mix": "not hash" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
// type check fail
|
// type check fail
|
||||||
failingInput = json::parse(R"({ "mix": 1213 })");
|
failingInput = json::parse(R"({ "mix": 1213 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} , "mix2": 1213 })");
|
failingInput = json::parse(R"({ "mix": {"limit": 42, "limit2": 22} , "mix2": 1213 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, WithCustomError)
|
TEST_F(RPCBaseTest, WithCustomError)
|
||||||
@@ -284,19 +288,19 @@ TEST_F(RPCBaseTest, WithCustomError)
|
|||||||
WithCustomError{Uint256HexStringValidator, RPC::Status{ripple::rpcBAD_FEATURE, "MyCustomError"}}},
|
WithCustomError{Uint256HexStringValidator, RPC::Status{ripple::rpcBAD_FEATURE, "MyCustomError"}}},
|
||||||
{"other", WithCustomError{Type<std::string>{}, RPC::Status{ripple::rpcALREADY_MULTISIG, "MyCustomError2"}}}};
|
{"other", WithCustomError{Type<std::string>{}, RPC::Status{ripple::rpcALREADY_MULTISIG, "MyCustomError2"}}}};
|
||||||
|
|
||||||
auto const passingInput = json::parse(
|
auto passingInput = json::parse(
|
||||||
R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "other": "1"})");
|
R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC", "other": "1"})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput =
|
auto failingInput =
|
||||||
json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B"})");
|
json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515B"})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "MyCustomError");
|
ASSERT_EQ(err.error().message, "MyCustomError");
|
||||||
ASSERT_EQ(err.error(), ripple::rpcBAD_FEATURE);
|
ASSERT_EQ(err.error(), ripple::rpcBAD_FEATURE);
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "other": 1})");
|
failingInput = json::parse(R"({ "other": 1})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "MyCustomError2");
|
ASSERT_EQ(err.error().message, "MyCustomError2");
|
||||||
ASSERT_EQ(err.error(), ripple::rpcALREADY_MULTISIG);
|
ASSERT_EQ(err.error(), ripple::rpcALREADY_MULTISIG);
|
||||||
@@ -318,10 +322,10 @@ TEST_F(RPCBaseTest, CustomValidator)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "taker": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" })");
|
auto passingInput = json::parse(R"({ "taker": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "taker": "wrongformat" })");
|
auto failingInput = json::parse(R"({ "taker": "wrongformat" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, NotSupported)
|
TEST_F(RPCBaseTest, NotSupported)
|
||||||
@@ -332,13 +336,13 @@ TEST_F(RPCBaseTest, NotSupported)
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "taker": 2 })");
|
auto passingInput = json::parse(R"({ "taker": 2 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "taker": 123 })");
|
auto failingInput = json::parse(R"({ "taker": 123 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "taker": 2, "getter": 2 })");
|
failingInput = json::parse(R"({ "taker": 2, "getter": 2 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, LedgerIndexValidator)
|
TEST_F(RPCBaseTest, LedgerIndexValidator)
|
||||||
@@ -347,21 +351,21 @@ TEST_F(RPCBaseTest, LedgerIndexValidator)
|
|||||||
{"ledgerIndex", LedgerIndexValidator},
|
{"ledgerIndex", LedgerIndexValidator},
|
||||||
};
|
};
|
||||||
auto passingInput = json::parse(R"({ "ledgerIndex": "validated" })");
|
auto passingInput = json::parse(R"({ "ledgerIndex": "validated" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
passingInput = json::parse(R"({ "ledgerIndex": "256" })");
|
passingInput = json::parse(R"({ "ledgerIndex": "256" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
passingInput = json::parse(R"({ "ledgerIndex": 256 })");
|
passingInput = json::parse(R"({ "ledgerIndex": 256 })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "ledgerIndex": "wrongformat" })");
|
auto failingInput = json::parse(R"({ "ledgerIndex": "wrongformat" })");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "ledgerIndexMalformed");
|
ASSERT_EQ(err.error().message, "ledgerIndexMalformed");
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "ledgerIndex": true })");
|
failingInput = json::parse(R"({ "ledgerIndex": true })");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "ledgerIndexMalformed");
|
ASSERT_EQ(err.error().message, "ledgerIndexMalformed");
|
||||||
}
|
}
|
||||||
@@ -372,20 +376,20 @@ TEST_F(RPCBaseTest, AccountValidator)
|
|||||||
{"account", AccountValidator},
|
{"account", AccountValidator},
|
||||||
};
|
};
|
||||||
auto failingInput = json::parse(R"({ "account": 256 })");
|
auto failingInput = json::parse(R"({ "account": 256 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })");
|
failingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jp" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })");
|
failingInput = json::parse(R"({ "account": "02000000000000000000000000000000000000000000000000000000000000000" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })");
|
auto passingInput = json::parse(R"({ "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
passingInput =
|
passingInput =
|
||||||
json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })");
|
json::parse(R"({ "account": "020000000000000000000000000000000000000000000000000000000000000000" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, AccountMarkerValidator)
|
TEST_F(RPCBaseTest, AccountMarkerValidator)
|
||||||
@@ -394,32 +398,32 @@ TEST_F(RPCBaseTest, AccountMarkerValidator)
|
|||||||
{"marker", AccountMarkerValidator},
|
{"marker", AccountMarkerValidator},
|
||||||
};
|
};
|
||||||
auto failingInput = json::parse(R"({ "marker": 256 })");
|
auto failingInput = json::parse(R"({ "marker": 256 })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "marker": "testtest" })");
|
failingInput = json::parse(R"({ "marker": "testtest" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "marker": "ABAB1234:1H" })");
|
failingInput = json::parse(R"({ "marker": "ABAB1234:1H" })");
|
||||||
ASSERT_FALSE(spec.validate(failingInput));
|
ASSERT_FALSE(spec.process(failingInput));
|
||||||
|
|
||||||
auto passingInput = json::parse(R"({ "account": "ABAB1234:123" })");
|
auto passingInput = json::parse(R"({ "account": "ABAB1234:123" })");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCBaseTest, Uint256HexStringValidator)
|
TEST_F(RPCBaseTest, Uint256HexStringValidator)
|
||||||
{
|
{
|
||||||
auto const spec = RpcSpec{{"transaction", Uint256HexStringValidator}};
|
auto const spec = RpcSpec{{"transaction", Uint256HexStringValidator}};
|
||||||
auto const passingInput =
|
auto passingInput =
|
||||||
json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC"})");
|
json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC"})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "transaction": 256})");
|
auto failingInput = json::parse(R"({ "transaction": 256})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "transactionNotString");
|
ASSERT_EQ(err.error().message, "transactionNotString");
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC"})");
|
failingInput = json::parse(R"({ "transaction": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC"})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "transactionMalformed");
|
ASSERT_EQ(err.error().message, "transactionMalformed");
|
||||||
}
|
}
|
||||||
@@ -428,18 +432,18 @@ TEST_F(RPCBaseTest, CurrencyValidator)
|
|||||||
{
|
{
|
||||||
auto const spec = RpcSpec{{"currency", CurrencyValidator}};
|
auto const spec = RpcSpec{{"currency", CurrencyValidator}};
|
||||||
auto passingInput = json::parse(R"({ "currency": "GBP"})");
|
auto passingInput = json::parse(R"({ "currency": "GBP"})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
passingInput = json::parse(R"({ "currency": "0158415500000000C1F76FF6ECB0BAC600000000"})");
|
passingInput = json::parse(R"({ "currency": "0158415500000000C1F76FF6ECB0BAC600000000"})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "currency": 256})");
|
auto failingInput = json::parse(R"({ "currency": 256})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "currencyNotString");
|
ASSERT_EQ(err.error().message, "currencyNotString");
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "currency": "12314"})");
|
failingInput = json::parse(R"({ "currency": "12314"})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "malformedCurrency");
|
ASSERT_EQ(err.error().message, "malformedCurrency");
|
||||||
}
|
}
|
||||||
@@ -448,15 +452,15 @@ TEST_F(RPCBaseTest, IssuerValidator)
|
|||||||
{
|
{
|
||||||
auto const spec = RpcSpec{{"issuer", IssuerValidator}};
|
auto const spec = RpcSpec{{"issuer", IssuerValidator}};
|
||||||
auto passingInput = json::parse(R"({ "issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})");
|
auto passingInput = json::parse(R"({ "issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "issuer": 256})");
|
auto failingInput = json::parse(R"({ "issuer": 256})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
ASSERT_EQ(err.error().message, "issuerNotString");
|
ASSERT_EQ(err.error().message, "issuerNotString");
|
||||||
|
|
||||||
failingInput = json::parse(fmt::format(R"({{ "issuer": "{}"}})", toBase58(ripple::noAccount())));
|
failingInput = json::parse(fmt::format(R"({{ "issuer": "{}"}})", toBase58(ripple::noAccount())));
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,18 +480,18 @@ TEST_F(RPCBaseTest, SubscribeStreamValidator)
|
|||||||
"book_changes"
|
"book_changes"
|
||||||
]
|
]
|
||||||
})");
|
})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "streams": 256})");
|
auto failingInput = json::parse(R"({ "streams": 256})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "streams": ["test"]})");
|
failingInput = json::parse(R"({ "streams": ["test"]})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "streams": [123]})");
|
failingInput = json::parse(R"({ "streams": [123]})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,17 +500,35 @@ TEST_F(RPCBaseTest, SubscribeAccountsValidator)
|
|||||||
auto const spec = RpcSpec{{"accounts", SubscribeAccountsValidator}};
|
auto const spec = RpcSpec{{"accounts", SubscribeAccountsValidator}};
|
||||||
auto passingInput =
|
auto passingInput =
|
||||||
json::parse(R"({ "accounts": ["rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"]})");
|
json::parse(R"({ "accounts": ["rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn","rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun"]})");
|
||||||
ASSERT_TRUE(spec.validate(passingInput));
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
auto failingInput = json::parse(R"({ "accounts": 256})");
|
auto failingInput = json::parse(R"({ "accounts": 256})");
|
||||||
auto err = spec.validate(failingInput);
|
auto err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "accounts": ["test"]})");
|
failingInput = json::parse(R"({ "accounts": ["test"]})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
|
|
||||||
failingInput = json::parse(R"({ "accounts": [123]})");
|
failingInput = json::parse(R"({ "accounts": [123]})");
|
||||||
err = spec.validate(failingInput);
|
err = spec.process(failingInput);
|
||||||
ASSERT_FALSE(err);
|
ASSERT_FALSE(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCBaseTest, ClampingModifier)
|
||||||
|
{
|
||||||
|
auto spec = RpcSpec{
|
||||||
|
{"amount", Clamp<uint32_t>{10u, 20u}},
|
||||||
|
};
|
||||||
|
|
||||||
|
auto passingInput = json::parse(R"({ "amount": 15 })");
|
||||||
|
ASSERT_TRUE(spec.process(passingInput));
|
||||||
|
|
||||||
|
auto passingInput2 = json::parse(R"({ "amount": 5 })");
|
||||||
|
ASSERT_TRUE(spec.process(passingInput2));
|
||||||
|
ASSERT_EQ(passingInput2.at("amount").as_uint64(), 10u); // clamped
|
||||||
|
|
||||||
|
auto passingInput3 = json::parse(R"({ "amount": 25 })");
|
||||||
|
ASSERT_TRUE(spec.process(passingInput3));
|
||||||
|
ASSERT_EQ(passingInput3.at("amount").as_uint64(), 20u); // clamped
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user