mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-27 23:25:53 +00:00
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
103
src/rpc/common/AnyHandler.h
Normal file
103
src/rpc/common/AnyHandler.h
Normal file
@@ -0,0 +1,103 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/Types.h>
|
||||
#include <rpc/common/impl/Processors.h>
|
||||
|
||||
namespace RPCng {
|
||||
|
||||
/**
|
||||
* @brief A type-erased Handler that can contain any (NextGen) RPC handler class
|
||||
*/
|
||||
class AnyHandler final
|
||||
{
|
||||
public:
|
||||
template <
|
||||
Handler HandlerType,
|
||||
typename ProcessingStrategy = detail::DefaultProcessor<HandlerType>>
|
||||
/* implicit */ AnyHandler(HandlerType&& handler)
|
||||
: pimpl_{std::make_unique<Model<HandlerType, ProcessingStrategy>>(
|
||||
std::forward<HandlerType>(handler))}
|
||||
{
|
||||
}
|
||||
|
||||
~AnyHandler() = default;
|
||||
AnyHandler(AnyHandler const& other) : pimpl_{other.pimpl_->clone()}
|
||||
{
|
||||
}
|
||||
AnyHandler&
|
||||
operator=(AnyHandler const& rhs)
|
||||
{
|
||||
AnyHandler copy{rhs};
|
||||
pimpl_.swap(copy.pimpl_);
|
||||
return *this;
|
||||
}
|
||||
AnyHandler(AnyHandler&&) = default;
|
||||
AnyHandler&
|
||||
operator=(AnyHandler&&) = default;
|
||||
|
||||
[[nodiscard]] ReturnType
|
||||
process(boost::json::value const& value) const
|
||||
{
|
||||
return pimpl_->process(value);
|
||||
}
|
||||
|
||||
private:
|
||||
struct Concept
|
||||
{
|
||||
virtual ~Concept() = default;
|
||||
|
||||
[[nodiscard]] virtual ReturnType
|
||||
process(boost::json::value const& value) const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<Concept>
|
||||
clone() const = 0;
|
||||
};
|
||||
|
||||
template <typename HandlerType, typename ProcessorType>
|
||||
struct Model : Concept
|
||||
{
|
||||
HandlerType handler;
|
||||
ProcessorType processor;
|
||||
|
||||
Model(HandlerType&& handler) : handler{std::move(handler)}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] ReturnType
|
||||
process(boost::json::value const& value) const override
|
||||
{
|
||||
return processor(handler, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Concept>
|
||||
clone() const override
|
||||
{
|
||||
return std::make_unique<Model>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<Concept> pimpl_;
|
||||
};
|
||||
|
||||
} // namespace RPCng
|
||||
60
src/rpc/common/Concepts.h
Normal file
60
src/rpc/common/Concepts.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/Types.h>
|
||||
|
||||
#include <boost/json/value_from.hpp>
|
||||
#include <boost/json/value_to.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace RPCng {
|
||||
|
||||
struct RpcSpec;
|
||||
|
||||
/**
|
||||
* @brief A concept that specifies what a requirement used with @ref FieldSpec
|
||||
* must provide
|
||||
*/
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
concept Requirement = requires(T a) {
|
||||
{ a.verify(boost::json::value{}, std::string{}) } -> std::same_as<MaybeError>;
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @brief A concept that specifies what a Handler type must provide
|
||||
*
|
||||
* Note that value_from and value_to should be implemented using tag_invoke
|
||||
* as per boost::json documentation for these functions.
|
||||
*/
|
||||
// clang-format off
|
||||
template <typename T>
|
||||
concept Handler = requires(T a, typename T::Input in, typename T::Output out) {
|
||||
{ a.spec() } -> std::same_as<RpcSpecConstRef>;
|
||||
{ a.process(in) } -> std::same_as<HandlerReturnType<decltype(out)>>;
|
||||
}
|
||||
&& boost::json::has_value_from<typename T::Output>::value
|
||||
&& boost::json::has_value_to<typename T::Input>::value;
|
||||
// clang-format on
|
||||
|
||||
} // namespace RPCng
|
||||
42
src/rpc/common/Specs.cpp
Normal file
42
src/rpc/common/Specs.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/common/Specs.h>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
namespace RPCng {
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
FieldSpec::validate(boost::json::value const& value) const
|
||||
{
|
||||
return validator_(value);
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
RpcSpec::validate(boost::json::value const& value) const
|
||||
{
|
||||
for (auto const& field : fields_)
|
||||
if (auto ret = field.validate(value); not ret)
|
||||
return Error{ret.error()};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace RPCng
|
||||
70
src/rpc/common/Specs.h
Normal file
70
src/rpc/common/Specs.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/Types.h>
|
||||
#include <rpc/common/impl/Factories.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace RPCng {
|
||||
|
||||
/**
|
||||
* @brief Represents a Specification for one field of an RPC command
|
||||
*/
|
||||
struct FieldSpec final
|
||||
{
|
||||
template <Requirement... Requirements>
|
||||
FieldSpec(std::string const& key, Requirements&&... requirements)
|
||||
: validator_{detail::makeFieldValidator<Requirements...>(
|
||||
key,
|
||||
std::forward<Requirements>(requirements)...)}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
validate(boost::json::value const& value) const;
|
||||
|
||||
private:
|
||||
std::function<MaybeError(boost::json::value const&)> validator_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a Specification of an entire RPC command
|
||||
*
|
||||
* Note: this should really be all constexpr and handlers would expose
|
||||
* static constexpr RpcSpec spec instead. Maybe some day in the future.
|
||||
*/
|
||||
struct RpcSpec final
|
||||
{
|
||||
RpcSpec(std::initializer_list<FieldSpec> fields) : fields_{fields}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
validate(boost::json::value const& value) const;
|
||||
|
||||
private:
|
||||
std::vector<FieldSpec> fields_;
|
||||
};
|
||||
|
||||
} // namespace RPCng
|
||||
56
src/rpc/common/Types.h
Normal file
56
src/rpc/common/Types.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/Errors.h>
|
||||
#include <util/Expected.h>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
namespace RPCng {
|
||||
|
||||
/**
|
||||
* @brief Return type used for Validators that can return error but don't have
|
||||
* specific value to return
|
||||
*/
|
||||
using MaybeError = util::Expected<void, RPC::Status>;
|
||||
|
||||
/**
|
||||
* @brief The type that represents just the error part of @ref MaybeError
|
||||
*/
|
||||
using Error = util::Unexpected<RPC::Status>;
|
||||
|
||||
/**
|
||||
* @brief Return type for each individual handler
|
||||
*/
|
||||
template <typename OutputType>
|
||||
using HandlerReturnType = util::Expected<OutputType, RPC::Status>;
|
||||
|
||||
/**
|
||||
* @brief The final return type out of RPC engine
|
||||
*/
|
||||
using ReturnType = util::Expected<boost::json::value, RPC::Status>;
|
||||
|
||||
struct RpcSpec;
|
||||
struct FieldSpec;
|
||||
|
||||
using RpcSpecConstRef = RpcSpec const&;
|
||||
|
||||
} // namespace RPCng
|
||||
89
src/rpc/common/Validators.cpp
Normal file
89
src/rpc/common/Validators.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/common/Validators.h>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace RPCng::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());
|
||||
for (auto const& spec : specs)
|
||||
{
|
||||
if (auto const ret = spec.validate(res); not ret)
|
||||
return Error{ret.error()};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
Required::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 Error{RPC::Status{
|
||||
RPC::RippledError::rpcINVALID_PARAMS,
|
||||
"Required field '" + std::string{key} + "' missing"}};
|
||||
|
||||
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{RPC::Status{RPC::RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
auto const& arr = value.as_object().at(key.data()).as_array();
|
||||
if (idx_ >= arr.size())
|
||||
return Error{RPC::Status{RPC::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
|
||||
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()))
|
||||
return {}; // ignore. field does not exist, let 'required' fail
|
||||
// instead
|
||||
|
||||
return validator_(value.as_object().at(key.data()), key);
|
||||
}
|
||||
|
||||
} // namespace RPCng::validation
|
||||
259
src/rpc/common/Validators.h
Normal file
259
src/rpc/common/Validators.h
Normal file
@@ -0,0 +1,259 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 RPCng::validation {
|
||||
|
||||
/**
|
||||
* @brief Check that the type is the same as what was expected
|
||||
*
|
||||
* @tparam Expected The expected type that value should be convertible to
|
||||
* @param value The json value to check the type of
|
||||
* @return true if convertible; false otherwise
|
||||
*/
|
||||
template <typename Expected>
|
||||
[[nodiscard]] bool static checkType(boost::json::value const& value)
|
||||
{
|
||||
auto hasError = false;
|
||||
if constexpr (std::is_same_v<Expected, bool>)
|
||||
{
|
||||
if (not value.is_bool())
|
||||
hasError = true;
|
||||
}
|
||||
else if constexpr (std::is_same_v<Expected, std::string>)
|
||||
{
|
||||
if (not value.is_string())
|
||||
hasError = true;
|
||||
}
|
||||
else if constexpr (
|
||||
std::is_same_v<Expected, double> or std::is_same_v<Expected, float>)
|
||||
{
|
||||
if (not value.is_double())
|
||||
hasError = true;
|
||||
}
|
||||
else if constexpr (
|
||||
std::is_convertible_v<Expected, uint64_t> or
|
||||
std::is_convertible_v<Expected, int64_t>)
|
||||
{
|
||||
if (not value.is_int64() && not value.is_uint64())
|
||||
hasError = true;
|
||||
}
|
||||
else if constexpr (std::is_same_v<Expected, boost::json::array>)
|
||||
{
|
||||
if (not value.is_array())
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
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:
|
||||
explicit Section(std::initializer_list<FieldSpec> specs) : specs{specs}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A validator that simply requires a field to be present
|
||||
*/
|
||||
struct Required final
|
||||
{
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validates that the type of the value is one of the given types
|
||||
*/
|
||||
template <typename... Types>
|
||||
struct Type final
|
||||
{
|
||||
[[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
|
||||
|
||||
auto const& res = value.as_object().at(key.data());
|
||||
auto const convertible = (checkType<Types>(res) || ...);
|
||||
|
||||
if (not convertible)
|
||||
return Error{RPC::Status{RPC::RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validate that value is between specified min and max
|
||||
*/
|
||||
template <typename Type>
|
||||
class Between final
|
||||
{
|
||||
Type min_;
|
||||
Type max_;
|
||||
|
||||
public:
|
||||
explicit Between(Type min, Type max) : min_{min}, max_{max}
|
||||
{
|
||||
}
|
||||
|
||||
[[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
|
||||
|
||||
using boost::json::value_to;
|
||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||
// todo: may want a way to make this code more generic (e.g. use a free
|
||||
// function that can be overridden for this comparison)
|
||||
if (res < min_ || res > max_)
|
||||
return Error{RPC::Status{RPC::RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validates that the value is equal to the one passed in
|
||||
*/
|
||||
template <typename Type>
|
||||
class EqualTo final
|
||||
{
|
||||
Type original_;
|
||||
|
||||
public:
|
||||
explicit EqualTo(Type original) : original_{original}
|
||||
{
|
||||
}
|
||||
|
||||
[[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
|
||||
|
||||
using boost::json::value_to;
|
||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||
if (res != original_)
|
||||
return Error{RPC::Status{RPC::RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide to help disambiguate what it means to EqualTo a
|
||||
* "string" without specifying the type.
|
||||
*/
|
||||
EqualTo(char const*)->EqualTo<std::string>;
|
||||
|
||||
/**
|
||||
* @brief Validates that the value is one of the values passed in
|
||||
*/
|
||||
template <typename Type>
|
||||
class OneOf final
|
||||
{
|
||||
std::vector<Type> options_;
|
||||
|
||||
public:
|
||||
explicit OneOf(std::initializer_list<Type> options) : options_{options}
|
||||
{
|
||||
}
|
||||
|
||||
[[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
|
||||
|
||||
using boost::json::value_to;
|
||||
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_))
|
||||
return Error{RPC::Status{RPC::RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide to help disambiguate what it means to OneOf a
|
||||
* few "strings" without specifying the type.
|
||||
*/
|
||||
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:
|
||||
ValidateArrayAt(std::size_t idx, std::initializer_list<FieldSpec> specs)
|
||||
: idx_{idx}, specs_{specs}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A meta-validator that allows to specify a custom validation function
|
||||
*/
|
||||
class CustomValidator final
|
||||
{
|
||||
std::function<MaybeError(boost::json::value const&, std::string_view)>
|
||||
validator_;
|
||||
|
||||
public:
|
||||
template <typename Fn>
|
||||
explicit CustomValidator(Fn&& fn) : validator_{std::forward<Fn>(fn)}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
} // namespace RPCng::validation
|
||||
59
src/rpc/common/impl/Factories.h
Normal file
59
src/rpc/common/impl/Factories.h
Normal file
@@ -0,0 +1,59 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/Types.h>
|
||||
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace RPCng::detail {
|
||||
|
||||
template <Requirement... Requirements>
|
||||
[[nodiscard]] auto
|
||||
makeFieldValidator(std::string const& key, Requirements&&... requirements)
|
||||
{
|
||||
return [key, ... r = std::forward<Requirements>(requirements)](
|
||||
boost::json::value const& j) -> MaybeError {
|
||||
// clang-format off
|
||||
std::optional<RPC::Status> firstFailure = std::nullopt;
|
||||
|
||||
// This expands in order of Requirements and stops evaluating after
|
||||
// first failure which is stored in `firstFailure` and can be checked
|
||||
// later on to see whether the verification failed as a whole or not.
|
||||
([&j, &key, &firstFailure, req = &r]() {
|
||||
if (firstFailure)
|
||||
return; // already failed earlier - skip
|
||||
|
||||
if (auto const res = req->verify(j, key); not res)
|
||||
firstFailure = res.error();
|
||||
}(), ...);
|
||||
// clang-format on
|
||||
|
||||
if (firstFailure)
|
||||
return Error{firstFailure.value()};
|
||||
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace RPCng::detail
|
||||
52
src/rpc/common/impl/Processors.h
Normal file
52
src/rpc/common/impl/Processors.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/Types.h>
|
||||
|
||||
namespace RPCng::detail {
|
||||
|
||||
template <Handler HandlerType>
|
||||
struct DefaultProcessor final
|
||||
{
|
||||
[[nodiscard]] ReturnType
|
||||
operator()(HandlerType const& handler, boost::json::value const& value)
|
||||
const
|
||||
{
|
||||
using boost::json::value_from;
|
||||
using boost::json::value_to;
|
||||
|
||||
// first we run validation
|
||||
auto const spec = handler.spec();
|
||||
if (auto const ret = spec.validate(value); not ret)
|
||||
return Error{ret.error()}; // forward Status
|
||||
|
||||
auto const inData = value_to<typename HandlerType::Input>(value);
|
||||
|
||||
// real handler is given expected Input, not json
|
||||
if (auto const ret = handler.process(inData); not ret)
|
||||
return Error{ret.error()}; // forward Status
|
||||
else
|
||||
return value_from(ret.value());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace RPCng::detail
|
||||
264
src/util/Expected.h
Normal file
264
src/util/Expected.h
Normal file
@@ -0,0 +1,264 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2023, the clio developers.
|
||||
Copyright (c) 2021 Ripple Labs Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
*
|
||||
* This entire file is taken from rippled and modified slightly to fit this
|
||||
* codebase as well as fixing the original issue that made this necessary.
|
||||
*
|
||||
* The reason is that currently there is no easy way to injest the fix that is
|
||||
* required to make this implementation correctly work with boost::json::value.
|
||||
* Since this will be replaced by `std::expected` as soon as possible there is
|
||||
* not much harm done in doing it this way.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <boost/outcome.hpp>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace util {
|
||||
|
||||
/** Expected is an approximation of std::expected (hoped for in C++23)
|
||||
|
||||
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
|
||||
|
||||
The implementation is entirely based on boost::outcome_v2::result.
|
||||
*/
|
||||
|
||||
// Exception thrown by an invalid access to Expected.
|
||||
struct bad_expected_access : public std::runtime_error
|
||||
{
|
||||
bad_expected_access() : runtime_error("bad expected access")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Custom policy for Expected. Always throw on an invalid access.
|
||||
struct throw_policy : public boost::outcome_v2::policy::base
|
||||
{
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_value_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_value(std::forward<Impl>(self)))
|
||||
ripple::Throw<bad_expected_access>();
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_error_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_error(std::forward<Impl>(self)))
|
||||
ripple::Throw<bad_expected_access>();
|
||||
}
|
||||
|
||||
template <class Impl>
|
||||
static constexpr void
|
||||
wide_exception_check(Impl&& self)
|
||||
{
|
||||
if (!base::_has_exception(std::forward<Impl>(self)))
|
||||
ripple::Throw<bad_expected_access>();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Definition of Unexpected, which is used to construct the unexpected
|
||||
// return type of an Expected.
|
||||
template <class E>
|
||||
class Unexpected
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_same<E, void>::value, "E must not be void");
|
||||
|
||||
Unexpected() = delete;
|
||||
|
||||
constexpr explicit Unexpected(E const& e) : val_(e)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr explicit Unexpected(E&& e) : val_(std::move(e))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr const E&
|
||||
value() const&
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
constexpr E&
|
||||
value() &
|
||||
{
|
||||
return val_;
|
||||
}
|
||||
|
||||
constexpr E&&
|
||||
value() &&
|
||||
{
|
||||
return std::move(val_);
|
||||
}
|
||||
|
||||
constexpr const E&&
|
||||
value() const&&
|
||||
{
|
||||
return std::move(val_);
|
||||
}
|
||||
|
||||
private:
|
||||
E val_;
|
||||
};
|
||||
|
||||
// Unexpected deduction guide that converts array to const*.
|
||||
template <typename E, std::size_t N>
|
||||
Unexpected(E (&)[N]) -> Unexpected<E const*>;
|
||||
|
||||
// Definition of Expected. All of the machinery comes from boost::result.
|
||||
template <class T, class E>
|
||||
class [[nodiscard]] Expected
|
||||
: private boost::outcome_v2::result<T, E, detail::throw_policy>
|
||||
{
|
||||
using Base = boost::outcome_v2::result<T, E, detail::throw_policy>;
|
||||
|
||||
public:
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, T>>>
|
||||
constexpr Expected(U r) : Base(T(std::forward<U>(r)))
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, E>>>
|
||||
constexpr Expected(Unexpected<U> e) : Base(E(std::forward<U>(e.value())))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
has_value() const
|
||||
{
|
||||
return Base::has_value();
|
||||
}
|
||||
|
||||
constexpr T const&
|
||||
value() const
|
||||
{
|
||||
return Base::value();
|
||||
}
|
||||
|
||||
constexpr T&
|
||||
value()
|
||||
{
|
||||
return Base::value();
|
||||
}
|
||||
|
||||
constexpr E const&
|
||||
error() const
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr E&
|
||||
error()
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return has_value();
|
||||
}
|
||||
|
||||
// Add operator* and operator-> so the Expected API looks a bit more like
|
||||
// what std::expected is likely to look like. See:
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0323r10.html
|
||||
[[nodiscard]] constexpr T&
|
||||
operator*()
|
||||
{
|
||||
return this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T const&
|
||||
operator*() const
|
||||
{
|
||||
return this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T*
|
||||
operator->()
|
||||
{
|
||||
return &this->value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T const*
|
||||
operator->() const
|
||||
{
|
||||
return &this->value();
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization of Expected<void, E>. Allows returning either success
|
||||
// (without a value) or the reason for the failure.
|
||||
template <class E>
|
||||
class [[nodiscard]] Expected<void, E>
|
||||
: private boost::outcome_v2::result<void, E, detail::throw_policy>
|
||||
{
|
||||
using Base = boost::outcome_v2::result<void, E, detail::throw_policy>;
|
||||
|
||||
public:
|
||||
// The default constructor makes a successful Expected<void, E>.
|
||||
// This aligns with std::expected behavior proposed in P0323R10.
|
||||
constexpr Expected() : Base(boost::outcome_v2::success())
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename U,
|
||||
typename = std::enable_if_t<std::is_convertible_v<U, E>>>
|
||||
constexpr Expected(Unexpected<U> e) : Base(E(std::forward<U>(e.value())))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr E const&
|
||||
error() const
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr E&
|
||||
error()
|
||||
{
|
||||
return Base::error();
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const
|
||||
{
|
||||
return Base::has_value();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
Reference in New Issue
Block a user