Merge bitcoin/bitcoin#29277: RPC: access RPC arguments by name

30a6c99935 rpc: access some args by name (stickies-v)
bbb31269bf rpc: add named arg helper (stickies-v)
13525e0c24 rpc: add arg helper unit test (stickies-v)

Pull request description:

  Adds string overloads for the `RPCHelpMan::Arg` and `RPCHelpMan::MaybeArg` helpers to be able to access RPC arguments by name instead of index number. Especially in RPCs with a large number of parameters, this can be quite helpful.

  Example usage:
  ```cpp
  const auto action{self.Arg<std::string>("action")};
  ```

  Most of the LoC is adding test coverage and documentation updates. No behaviour change.

  An alternative approach to #27788 with significantly less overhaul.

ACKs for top commit:
  fjahr:
    Code review ACK 30a6c99935
  maflcko:
    ACK 30a6c99935 🥑
  ryanofsky:
    Code review ACK 30a6c99935. Nice change! Implementation is surprisingly simple and additional unit test coverage is welcome, too.

Tree-SHA512: 4904f5f914fe1d421d32f60edb7c5a028c8ea0f140a2f207a106b4752d441164e073066a6bf2e17693f859fe847815a96609d3cf521e0ac4178d8cd09362ea3d
This commit is contained in:
Ryan Ofsky
2024-04-29 10:14:38 -04:00
8 changed files with 149 additions and 29 deletions

View File

@@ -582,4 +582,72 @@ BOOST_AUTO_TEST_CASE(help_example)
BOOST_CHECK_NE(HelpExampleRpcNamed("foo", {{"arg", true}}), HelpExampleRpcNamed("foo", {{"arg", "true"}}));
}
static void CheckRpc(const std::vector<RPCArg>& params, const UniValue& args, RPCHelpMan::RPCMethodImpl test_impl)
{
auto null_result{RPCResult{RPCResult::Type::NONE, "", "None"}};
const RPCHelpMan rpc{"dummy", "dummy description", params, null_result, RPCExamples{""}, test_impl};
JSONRPCRequest req;
req.params = args;
rpc.HandleRequest(req);
}
BOOST_AUTO_TEST_CASE(rpc_arg_helper)
{
constexpr bool DEFAULT_BOOL = true;
constexpr auto DEFAULT_STRING = "default";
constexpr uint64_t DEFAULT_UINT64_T = 3;
//! Parameters with which the RPCHelpMan is instantiated
const std::vector<RPCArg> params{
// Required arg
{"req_int", RPCArg::Type::NUM, RPCArg::Optional::NO, ""},
{"req_str", RPCArg::Type::STR, RPCArg::Optional::NO, ""},
// Default arg
{"def_uint64_t", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_UINT64_T}, ""},
{"def_string", RPCArg::Type::STR, RPCArg::Default{DEFAULT_STRING}, ""},
{"def_bool", RPCArg::Type::BOOL, RPCArg::Default{DEFAULT_BOOL}, ""},
// Optional arg without default
{"opt_double", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, ""},
{"opt_string", RPCArg::Type::STR, RPCArg::Optional::OMITTED, ""}
};
//! Check that `self.Arg` returns the same value as the `request.params` accessors
RPCHelpMan::RPCMethodImpl check_positional = [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
BOOST_CHECK_EQUAL(self.Arg<int>(0), request.params[0].getInt<int>());
BOOST_CHECK_EQUAL(self.Arg<std::string>(1), request.params[1].get_str());
BOOST_CHECK_EQUAL(self.Arg<uint64_t>(2), request.params[2].isNull() ? DEFAULT_UINT64_T : request.params[2].getInt<uint64_t>());
BOOST_CHECK_EQUAL(self.Arg<std::string>(3), request.params[3].isNull() ? DEFAULT_STRING : request.params[3].get_str());
BOOST_CHECK_EQUAL(self.Arg<bool>(4), request.params[4].isNull() ? DEFAULT_BOOL : request.params[4].get_bool());
if (!request.params[5].isNull()) {
BOOST_CHECK_EQUAL(self.MaybeArg<double>(5).value(), request.params[5].get_real());
} else {
BOOST_CHECK(!self.MaybeArg<double>(5));
}
if (!request.params[6].isNull()) {
BOOST_CHECK(self.MaybeArg<std::string>(6));
BOOST_CHECK_EQUAL(*self.MaybeArg<std::string>(6), request.params[6].get_str());
} else {
BOOST_CHECK(!self.MaybeArg<std::string>(6));
}
return UniValue{};
};
CheckRpc(params, UniValue{JSON(R"([5, "hello", null, null, null, null, null])")}, check_positional);
CheckRpc(params, UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")}, check_positional);
//! Check that `self.Arg` returns the same value when using index and key
RPCHelpMan::RPCMethodImpl check_named = [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
BOOST_CHECK_EQUAL(self.Arg<int>(0), self.Arg<int>("req_int"));
BOOST_CHECK_EQUAL(self.Arg<std::string>(1), self.Arg<std::string>("req_str"));
BOOST_CHECK_EQUAL(self.Arg<uint64_t>(2), self.Arg<uint64_t>("def_uint64_t"));
BOOST_CHECK_EQUAL(self.Arg<std::string>(3), self.Arg<std::string>("def_string"));
BOOST_CHECK_EQUAL(self.Arg<bool>(4), self.Arg<bool>("def_bool"));
BOOST_CHECK(self.MaybeArg<double>(5) == self.MaybeArg<double>("opt_double"));
BOOST_CHECK(self.MaybeArg<std::string>(6) == self.MaybeArg<std::string>("opt_string"));
return UniValue{};
};
CheckRpc(params, UniValue{JSON(R"([5, "hello", null, null, null, null, null])")}, check_named);
CheckRpc(params, UniValue{JSON(R"([5, "hello", 4, "test", true, 1.23, "world"])")}, check_named);
}
BOOST_AUTO_TEST_SUITE_END()