From 09ac1b866e12d894fb75cf2a131777000fdc2199 Mon Sep 17 00:00:00 2001 From: cyan317 <120398799+cindyyan317@users.noreply.github.com> Date: Wed, 8 Feb 2023 16:20:24 +0000 Subject: [PATCH] Add ping handler (#503) Fix #506 --- CMakeLists.txt | 3 +- src/rpc/common/Concepts.h | 19 ++++++--- src/rpc/common/Types.h | 10 +++++ src/rpc/common/impl/Processors.h | 39 ++++++++++++++----- src/rpc/ngHandlers/Ping.h | 38 ++++++++++++++++++ .../rpc/handlers/DefaultProcessorTests.cpp | 13 +++++++ unittests/rpc/handlers/PingTest.cpp | 37 ++++++++++++++++++ unittests/rpc/handlers/TestHandlerTests.cpp | 10 +++++ unittests/rpc/handlers/impl/FakesAndMocks.h | 21 ++++++++++ 9 files changed, 174 insertions(+), 16 deletions(-) create mode 100644 src/rpc/ngHandlers/Ping.h create mode 100644 unittests/rpc/handlers/PingTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6836da9e..8a1fef16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,7 +112,8 @@ if(BUILD_TESTS) unittests/rpc/ErrorTests.cpp unittests/rpc/BaseTests.cpp unittests/rpc/handlers/TestHandlerTests.cpp - unittests/rpc/handlers/DefaultProcessorTests.cpp) + unittests/rpc/handlers/DefaultProcessorTests.cpp + unittests/rpc/handlers/PingTest.cpp) include(CMake/deps/gtest.cmake) # if CODE_COVERAGE enable, add clio_test-ccov diff --git a/src/rpc/common/Concepts.h b/src/rpc/common/Concepts.h index bbfed99e..c483db52 100644 --- a/src/rpc/common/Concepts.h +++ b/src/rpc/common/Concepts.h @@ -49,12 +49,21 @@ concept Requirement = requires(T a) { */ // clang-format off template -concept Handler = requires(T a, typename T::Input in, typename T::Output out) { +concept HandlerWithInput = requires(T a, typename T::Input in, typename T::Output out) { { a.spec() } -> std::same_as; - { a.process(in) } -> std::same_as>; -} -&& boost::json::has_value_from::value -&& boost::json::has_value_to::value; + { a.process(in) } -> std::same_as>;} + && boost::json::has_value_to::value; + +template +concept HandlerWithoutInput = requires(T a, typename T::Output out) { + { a.process() } -> std::same_as>;}; + +template +concept Handler = +(HandlerWithInput +|| +HandlerWithoutInput) +&& boost::json::has_value_from::value; // clang-format on } // namespace RPCng diff --git a/src/rpc/common/Types.h b/src/rpc/common/Types.h index b62a8ad7..abb6f42e 100644 --- a/src/rpc/common/Types.h +++ b/src/rpc/common/Types.h @@ -53,4 +53,14 @@ struct FieldSpec; using RpcSpecConstRef = RpcSpec const&; +struct VoidOutput +{ +}; + +inline void +tag_invoke(boost::json::value_from_tag, boost::json::value& jv, VoidOutput) +{ + jv = boost::json::object{}; +} + } // namespace RPCng diff --git a/src/rpc/common/impl/Processors.h b/src/rpc/common/impl/Processors.h index d0724a3e..215f6ed8 100644 --- a/src/rpc/common/impl/Processors.h +++ b/src/rpc/common/impl/Processors.h @@ -24,6 +24,9 @@ namespace RPCng::detail { +template +static constexpr bool unsupported_handler_v = false; + template struct DefaultProcessor final { @@ -33,19 +36,35 @@ struct DefaultProcessor final { using boost::json::value_from; using boost::json::value_to; + if constexpr (HandlerWithInput) + { + // first we run validation + auto const spec = handler.spec(); + if (auto const ret = spec.validate(value); not ret) + return Error{ret.error()}; // forward Status - // 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(value); - auto const inData = value_to(value); - - // real handler is given expected Input, not json - if (auto const ret = handler.process(inData); not ret) - return Error{ret.error()}; // forward Status + // 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()); + } + else if constexpr (HandlerWithoutInput) + { + // no input to pass, ignore the value + if (auto const ret = handler.process(); not ret) + return Error{ret.error()}; // forward Status + else + return value_from(ret.value()); + } else - return value_from(ret.value()); + { + // when concept HandlerWithInput and HandlerWithoutInput not cover + // all Handler case + static_assert(unsupported_handler_v); + } } }; diff --git a/src/rpc/ngHandlers/Ping.h b/src/rpc/ngHandlers/Ping.h new file mode 100644 index 00000000..1fd769cd --- /dev/null +++ b/src/rpc/ngHandlers/Ping.h @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +/* + 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 + +namespace RPCng { + +class PingHandler +{ +public: + using Output = VoidOutput; + using Result = HandlerReturnType; + + Result + process() const + { + return Output{}; + } +}; +} // namespace RPCng diff --git a/unittests/rpc/handlers/DefaultProcessorTests.cpp b/unittests/rpc/handlers/DefaultProcessorTests.cpp index a79f6305..d08ff2cf 100644 --- a/unittests/rpc/handlers/DefaultProcessorTests.cpp +++ b/unittests/rpc/handlers/DefaultProcessorTests.cpp @@ -52,6 +52,19 @@ TEST_F(RPCDefaultProcessorTest, ValidInput) ASSERT_TRUE(ret); // no error } +TEST_F(RPCDefaultProcessorTest, NoInputVaildCall) +{ + HandlerWithoutInputMock handler; + RPCng::detail::DefaultProcessor processor; + + auto const data = InOutFake{"works"}; + auto const input = json::parse(R"({})"); + EXPECT_CALL(handler, process()).WillOnce(Return(data)); + + auto const ret = processor(handler, input); + ASSERT_TRUE(ret); // no error +} + TEST_F(RPCDefaultProcessorTest, InvalidInput) { HandlerMock handler; diff --git a/unittests/rpc/handlers/PingTest.cpp b/unittests/rpc/handlers/PingTest.cpp new file mode 100644 index 00000000..2d926b59 --- /dev/null +++ b/unittests/rpc/handlers/PingTest.cpp @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------------ +/* + 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 +#include +#include + +using namespace RPCng; + +class RPCHandlerTest : public NoLoggerFixture +{ +}; + +// example handler tests +TEST_F(RPCHandlerTest, Ping) +{ + auto const handler = AnyHandler{PingHandler{}}; + auto const output = handler.process(boost::json::parse(R"({})")); + ASSERT_TRUE(output); + EXPECT_EQ(output.value(), boost::json::parse(R"({})")); +} diff --git a/unittests/rpc/handlers/TestHandlerTests.cpp b/unittests/rpc/handlers/TestHandlerTests.cpp index adceb39a..bf4f37ce 100644 --- a/unittests/rpc/handlers/TestHandlerTests.cpp +++ b/unittests/rpc/handlers/TestHandlerTests.cpp @@ -50,6 +50,16 @@ TEST_F(RPCTestHandlerTest, HandlerSuccess) EXPECT_EQ(val.as_object().at("computed").as_string(), "world_10"); } +TEST_F(RPCTestHandlerTest, NoInputHandlerSuccess) +{ + auto const handler = AnyHandler{NoInputHandlerFake{}}; + auto const output = handler.process(json::parse(R"({})")); + ASSERT_TRUE(output); + + auto const val = output.value(); + EXPECT_EQ(val.as_object().at("computed").as_string(), "test"); +} + TEST_F(RPCTestHandlerTest, HandlerErrorHandling) { auto const handler = AnyHandler{HandlerFake{}}; diff --git a/unittests/rpc/handlers/impl/FakesAndMocks.h b/unittests/rpc/handlers/impl/FakesAndMocks.h index ae04e3b5..4d2a7c49 100644 --- a/unittests/rpc/handlers/impl/FakesAndMocks.h +++ b/unittests/rpc/handlers/impl/FakesAndMocks.h @@ -98,6 +98,19 @@ public: } }; +class NoInputHandlerFake +{ +public: + using Output = TestOutput; + using Result = RPCng::HandlerReturnType; + + Result + process() const + { + return Output{"test"}; + } +}; + // example handler that returns custom error class FailingHandlerFake { @@ -165,4 +178,12 @@ struct HandlerMock MOCK_METHOD(Result, process, (Input), (const)); }; +struct HandlerWithoutInputMock +{ + using Output = InOutFake; + using Result = RPCng::HandlerReturnType; + + MOCK_METHOD(Result, process, (), (const)); +}; + } // namespace unittests::detail