plugin: implement addRedfishHandler This commit implements the first Redfish Plugin macro: REDFISH_HANDLER_ADD This plugin macro gives plugin user ability to add a platform specific route. The plugin takes URL, Verb, Redfish Privileges, and the callback. A few refacotring has been implemented (e.g., string_view instead of string reference) to make this happen. A unit test is added which demostrates how the plugin adds a new route. The test also ensures duplicate routes throws. Tested: unit test. Change-Id: Id15d79a681f74367ce5108c67d9da146eac9fef8 Signed-off-by: Nan Zhou <nanzhou@google.com>
diff --git a/http/app.hpp b/http/app.hpp index 85104fc..bd30007 100644 --- a/http/app.hpp +++ b/http/app.hpp
@@ -12,6 +12,7 @@ #include <boost/beast/ssl/ssl_stream.hpp> #endif +#include <array> #include <chrono> #include <cstdint> #include <functional> @@ -118,6 +119,13 @@ return router.getRoutes(parent); } + template <uint64_t Tag, typename Func, size_t N> + void addHandler(std::string_view url, boost::beast::http::verb verb, + const std::array<redfish::Privileges, N>& p, Func&& func) + { + router.newRuleTagged<Tag>(url).methods(verb).privileges(p)(func); + } + #ifdef BMCWEB_ENABLE_SSL App& ssl(std::shared_ptr<boost::asio::ssl::context>&& ctx) {
diff --git a/http/routing.hpp b/http/routing.hpp index 3c9d555..c60721c 100644 --- a/http/routing.hpp +++ b/http/routing.hpp
@@ -41,7 +41,7 @@ class BaseRule { public: - explicit BaseRule(const std::string& thisRule) : rule(thisRule) + explicit BaseRule(std::string_view thisRule) : rule(thisRule) {} virtual ~BaseRule() = default; @@ -596,7 +596,7 @@ public: using self_t = TaggedRule<Args...>; - explicit TaggedRule(const std::string& ruleIn) : BaseRule(ruleIn) + explicit TaggedRule(std::string_view ruleIn) : BaseRule(ruleIn) {} void validate() override @@ -806,7 +806,7 @@ template <uint64_t N> typename black_magic::Arguments<N>::type::template rebind<TaggedRule>& - newRuleTagged(const std::string& rule) + newRuleTagged(std::string_view rule) { using RuleT = typename black_magic::Arguments<N>::type::template rebind< TaggedRule>;
diff --git a/meson.build b/meson.build index 29e1740..0245754 100644 --- a/meson.build +++ b/meson.build
@@ -432,6 +432,7 @@ 'test/redfish-core/lib/thermal_subsystem_test.cpp', 'test/redfish-core/lib/power_subsystem_test.cpp', # 'test/redfish-core/lib/chassis_test.cpp', + 'plugins/macros_test.cpp', ) if(get_option('tests').enabled())
diff --git a/plugins/macros.hpp b/plugins/macros.hpp new file mode 100644 index 0000000..0d90605 --- /dev/null +++ b/plugins/macros.hpp
@@ -0,0 +1,26 @@ +#include "app_singleton.hpp" +#include "async_resp.hpp" +#include "http_request.hpp" +#include "privileges.hpp" +#include "utility.hpp" + +#include <boost/beast/http/verb.hpp> + +#include <array> +#include <string_view> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define REDFISH_HANDLER_ADD(url, verb, privileges, func) \ + plugins::addRedfishHandler<crow::black_magic::getParameterTag(url)>( \ + url, verb, privileges, func); + +namespace plugins +{ +template <uint64_t tag, typename RedfishHandler, size_t N> +void addRedfishHandler(std::string_view url, boost::beast::http::verb verb, + const std::array<redfish::Privileges, N>& p, + RedfishHandler&& func) +{ + crow::globalBmcWebApp->addHandler<tag>(url, verb, p, func); +} +} // namespace plugins \ No newline at end of file
diff --git a/plugins/macros_test.cpp b/plugins/macros_test.cpp new file mode 100644 index 0000000..ca15030 --- /dev/null +++ b/plugins/macros_test.cpp
@@ -0,0 +1,75 @@ +#include "app.hpp" +#include "app_singleton.hpp" +#include "async_resp.hpp" +#include "http_request.hpp" +#include "macros.hpp" +#include "privileges.hpp" +#include "registries/privilege_registry.hpp" +#include "utility.hpp" + +#include <boost/asio/io_context.hpp> +#include <boost/beast/http/verb.hpp> + +#include <memory> +#include <string_view> + +#include <gtest/gtest.h> + +namespace crow +{ +namespace +{ + +class PluginMacrosTest : public testing::Test +{ + public: + PluginMacrosTest() + { + io = std::make_shared<boost::asio::io_context>(); + app = std::make_unique<crow::App>(io); + crow::globalBmcWebApp = app.get(); + } + + private: + std::shared_ptr<boost::asio::io_context> io; + std::unique_ptr<crow::App> app; +}; + +TEST_F(PluginMacrosTest, AddHandlerCanBeInvoked) +{ + bool called = false; + auto callback = [&called](const Request&, + const std::shared_ptr<bmcweb::AsyncResp>&) { + called = true; + }; + + REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, + redfish::privileges::getChassis, std::move(callback)); + + crow::globalBmcWebApp->validate(); + + constexpr std::string_view url = "/hello/"; + std::error_code ec; + Request req{{boost::beast::http::verb::get, url, 11}, ec}; + + auto asyncResp = std::make_shared<bmcweb::AsyncResp>(); + crow::globalBmcWebApp->handle(req, asyncResp); + + EXPECT_TRUE(called); +} + +TEST_F(PluginMacrosTest, DuplicateHandlerThrows) +{ + auto callback = + [](const Request&, const std::shared_ptr<bmcweb::AsyncResp>&) {}; + + REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, + redfish::privileges::getChassis, std::move(callback)); + REDFISH_HANDLER_ADD("/hello/", boost::beast::http::verb::get, + redfish::privileges::getChassis, std::move(callback)); + + EXPECT_ANY_THROW(crow::globalBmcWebApp->validate()); +} + +} // namespace +} // namespace crow \ No newline at end of file