Document RPC framework (#501)

Fixes #500
This commit is contained in:
Alex Kremer
2023-02-03 12:07:51 +00:00
committed by GitHub
parent 19455b4d6c
commit c07e04ce84
4 changed files with 157 additions and 0 deletions

29
src/rpc/README.md Normal file
View File

@@ -0,0 +1,29 @@
# Clio RPC subsystem
## Background
The RPC subsystem is where the common framework for handling incoming JSON requests is implemented.
Currently the NextGen RPC framework is a work in progress and the handlers are not yet implemented using the new common framework classes.
## Integration plan
- Implement base framework - **done**
- Migrate handlers one by one, making them injectable, adding unit-tests - **in progress**
- Integrate all new handlers into clio in one go
- Cover the rest with unit-tests
- Release first time with new subsystem active
## Components
See `common` subfolder.
- **AnyHandler**: The type-erased wrapper that allows for storing different handlers in one map/vector.
- **RpcSpec/FieldSpec**: The RPC specification classes, used to specify how incoming JSON is to be validated before it's parsed and passed on to individual handler implementations.
- **Validators**: A bunch of supported validators that can be specified as requirements for each **`FieldSpec`** to make up the final **`RpcSpec`** of any given RPC handler.
## Implementing a (NextGen) handler
See `unittests/rpc` for exmaples.
Handlers need to fulfil the requirements specified by the **`Handler`** concept (see `rpc/common/Concepts.h`):
- Expose types:
* `Input` - The POD struct which acts as input for the handler
* `Output` - The POD struct which acts as output of a valid handler invocation
- Have a `spec()` member function returning a const reference to an **`RpcSpec`** describing the JSON input.
- Have a `process(Input)` member function that operates on `Input` POD and returns `HandlerReturnType<Output>`
- Implement `value_from` and `value_to` support using `tag_invoke` as per `boost::json` documentation for these functions.

View File

@@ -27,10 +27,23 @@ namespace RPCng {
/**
* @brief A type-erased Handler that can contain any (NextGen) RPC handler class
*
* This allows to store different handlers in one map/vector etc.
* Support for copying was added in order to allow storing in a
* map/unordered_map using the initializer_list constructor.
*/
class AnyHandler final
{
public:
/**
* @brief Type-erases any handler class.
*
* @tparam HandlerType The real type of wrapped handler class
* @tparam ProcessingStrategy A strategy that implements how processing of
* JSON is to be done
* @param handler The handler to wrap. Required to fulfil the @ref Handler
* concept.
*/
template <
Handler HandlerType,
typename ProcessingStrategy = detail::DefaultProcessor<HandlerType>>
@@ -55,6 +68,12 @@ public:
AnyHandler&
operator=(AnyHandler&&) = default;
/**
* @brief Process incoming JSON by the stored handler
*
* @param value The JSON to process
* @return JSON result or @ref RPC::Status on error
*/
[[nodiscard]] ReturnType
process(boost::json::value const& value) const
{

View File

@@ -33,6 +33,14 @@ namespace RPCng {
*/
struct FieldSpec final
{
/**
* @brief Construct a field specification out of a set of requirements
*
* @tparam Requirements The types of requirements @ref Requirement
* @param key The key in a JSON object that the field validates
* @param requirements The requirements, each of them have to fulfil
* the @ref Requirement concept
*/
template <Requirement... Requirements>
FieldSpec(std::string const& key, Requirements&&... requirements)
: validator_{detail::makeFieldValidator<Requirements...>(
@@ -41,6 +49,12 @@ struct FieldSpec final
{
}
/**
* @brief Validates the passed JSON value using the stored requirements
*
* @param value The JSON value to validate
* @return Nothing on success; @ref RPC::Status on error
*/
[[nodiscard]] MaybeError
validate(boost::json::value const& value) const;
@@ -56,10 +70,21 @@ private:
*/
struct RpcSpec final
{
/**
* @brief Construct a full RPC request specification
*
* @param fields The fields of the RPC specification @ref FieldSpec
*/
RpcSpec(std::initializer_list<FieldSpec> fields) : fields_{fields}
{
}
/**
* @brief Validates the passed JSON value using the stored field specs
*
* @param value The JSON value to validate
* @return Nothing on success; @ref RPC::Status on error
*/
[[nodiscard]] MaybeError
validate(boost::json::value const& value) const;

View File

@@ -76,10 +76,22 @@ 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;
};
@@ -99,6 +111,13 @@ struct Required final
template <typename... Types>
struct Type final
{
/**
* @brief Verify that the JSON value is (one) of specified type(s)
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer
* object
*/
[[nodiscard]] MaybeError
verify(boost::json::value const& value, std::string_view key) const
{
@@ -126,10 +145,23 @@ class Between final
Type max_;
public:
/**
* @brief Construct the validator storing min and max values
*
* @param min
* @param max
*/
explicit Between(Type min, Type max) : min_{min}, max_{max}
{
}
/**
* @brief Verify that the JSON value is within a certain range
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer
* object
*/
[[nodiscard]] MaybeError
verify(boost::json::value const& value, std::string_view key) const
{
@@ -157,10 +189,22 @@ class EqualTo final
Type original_;
public:
/**
* @brief Construct the validator with stored original value
*
* @param original The original value to store
*/
explicit EqualTo(Type original) : original_{original}
{
}
/**
* @brief Verify that the JSON value is equal to the stored original
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer
* object
*/
[[nodiscard]] MaybeError
verify(boost::json::value const& value, std::string_view key) const
{
@@ -192,10 +236,22 @@ class OneOf final
std::vector<Type> options_;
public:
/**
* @brief Construct the validator with stored options
*
* @param options The list of allowed options
*/
explicit OneOf(std::initializer_list<Type> options) : options_{options}
{
}
/**
* @brief Verify that the JSON value is one of the stored options
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer
* object
*/
[[nodiscard]] MaybeError
verify(boost::json::value const& value, std::string_view key) const
{
@@ -229,11 +285,25 @@ class ValidateArrayAt final
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;
};
@@ -247,11 +317,25 @@ class CustomValidator final
validator_;
public:
/**
* @brief Constructs a custom validator from any supported callable
*
* @tparam Fn The type of callable
* @param fn The callable/function object
*/
template <typename Fn>
explicit CustomValidator(Fn&& fn) : validator_{std::forward<Fn>(fn)}
{
}
/**
* @brief Verify that the JSON value is valid according to the custom
* validation function stored
*
* @param value The JSON value representing the outer object
* @param key The key used to retrieve the tested value from the outer
* object
*/
[[nodiscard]] MaybeError
verify(boost::json::value const& value, std::string_view key) const;
};