Add a method that decodes path parameters (#4126)

This commit is contained in:
Mahati Chamarthy 2022-08-15 14:21:53 +01:00 коммит произвёл GitHub
Родитель 66b537a9a1
Коммит 856df5be72
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 62 добавлений и 3 удалений

Просмотреть файл

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- New `GET /node/network/removable_nodes` and `DELETE /node/network/nodes/{node_id}` exposed to allow operator to decide which nodes can be safely shut down after retirement, and clear their state from the Key-Value Store.
- Added a new method `get_decoded_request_path_params` that returns a map of decoded path parameters (#4126)
### Fixed

Просмотреть файл

@ -72,6 +72,12 @@ namespace ccf
/// {"name": "bob", "age": "42"}
virtual const PathParams& get_request_path_params() = 0;
/// Decodes the path before returning a map of all PathParams.
/// For example, if the endpoint was installed at `/foo/{name}/{age}`, and
/// for the request path `/foo/bob%3A/42`, this would return the map:
/// {"name": "bob:", "age": "42"}
virtual const PathParams& get_decoded_request_path_params() = 0;
/// Returns map of all headers found in the request.
virtual const http::HeaderMap& get_request_headers() const = 0;

Просмотреть файл

@ -4,6 +4,7 @@
#include "ccf/endpoint_registry.h"
#include "ccf/common_auth_policies.h"
#include "http/http_parser.h"
#include "node/rpc/rpc_context_impl.h"
namespace ccf::endpoints
@ -279,7 +280,6 @@ namespace ccf::endpoints
kv::Tx&, ccf::RpcContext& rpc_ctx)
{
auto method = rpc_ctx.get_method();
auto endpoints_for_exact_method = fully_qualified_endpoints.find(method);
if (endpoints_for_exact_method != fully_qualified_endpoints.end())
{
@ -318,6 +318,7 @@ namespace ccf::endpoints
throw std::logic_error("Unexpected type of RpcContext");
}
auto& path_params = ctx_impl->path_params;
auto& decoded_path_params = ctx_impl->decoded_path_params;
for (size_t i = 0;
i < endpoint->spec.template_component_names.size();
++i)
@ -325,7 +326,9 @@ namespace ccf::endpoints
const auto& template_name =
endpoint->spec.template_component_names[i];
const auto& template_value = match[i + 1].str();
auto decoded_value = http::url_decode(template_value);
path_params[template_name] = template_value;
decoded_path_params[template_name] = decoded_value;
}
}

Просмотреть файл

@ -36,6 +36,12 @@ namespace ccf
return path_params;
}
ccf::PathParams decoded_path_params = {};
virtual const ccf::PathParams& get_decoded_request_path_params() override
{
return decoded_path_params;
}
bool is_create_request = false;
bool execute_on_node = false;

Просмотреть файл

@ -255,6 +255,23 @@ public:
}
};
class TestDecodedTemplatedPaths : public BaseTestFrontend
{
public:
TestDecodedTemplatedPaths(kv::Store& tables) : BaseTestFrontend(tables)
{
open();
auto endpoint = [this](auto& ctx) {
nlohmann::json response_body =
ctx.rpc_ctx->get_decoded_request_path_params();
ctx.rpc_ctx->set_response_body(response_body.dump(2));
ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
};
make_endpoint("/{foo}/{bar}/{baz}", HTTP_POST, endpoint).install();
}
};
class TestMemberFrontend : public MemberRpcFrontend
{
public:
@ -1060,7 +1077,7 @@ TEST_CASE("Templated paths")
TestTemplatedPaths frontend(*network.tables);
{
auto request = create_simple_request("/fin/fang/foom");
auto request = create_simple_request("/fin%3A/fang/foom");
const auto serialized_request = request.build_request();
auto rpc_ctx = ccf::make_rpc_context(user_session, serialized_request);
@ -1068,7 +1085,7 @@ TEST_CASE("Templated paths")
CHECK(response.status == HTTP_STATUS_OK);
std::map<std::string, std::string> expected_mapping;
expected_mapping["foo"] = "fin";
expected_mapping["foo"] = "fin%3A";
expected_mapping["bar"] = "fang";
expected_mapping["baz"] = "foom";
@ -1098,6 +1115,32 @@ TEST_CASE("Templated paths")
}
}
TEST_CASE("Decoded Templated paths")
{
NetworkState network;
prepare_callers(network);
TestDecodedTemplatedPaths frontend(*network.tables);
{
auto request = create_simple_request("/fin%3A/fang%2F/foom");
const auto serialized_request = request.build_request();
auto rpc_ctx = ccf::make_rpc_context(user_session, serialized_request);
auto response = parse_response(frontend.process(rpc_ctx).value());
CHECK(response.status == HTTP_STATUS_OK);
std::map<std::string, std::string> expected_mapping;
expected_mapping["foo"] = "fin:";
expected_mapping["bar"] = "fang/";
expected_mapping["baz"] = "foom";
const auto response_json = nlohmann::json::parse(response.body);
const auto actual_mapping = response_json.get<decltype(expected_mapping)>();
CHECK(expected_mapping == actual_mapping);
}
}
TEST_CASE("Signed read requests can be executed on backup")
{
NetworkState network;