diff options
author | John Edward Broadbent <jebr@google.com> | 2021-04-09 01:57:16 +0300 |
---|---|---|
committer | Ed Tanous <ed@tanous.net> | 2021-06-03 21:18:02 +0300 |
commit | 7e860f1550c8686eec42f7a75bc5f2ef51e756ad (patch) | |
tree | 989da47a8427bc1a60119f480e2523151b1433aa /redfish-core/lib/event_service.hpp | |
parent | eb75770c6c4369984cb150ded4f5ace410ed24a9 (diff) | |
download | bmcweb-7e860f1550c8686eec42f7a75bc5f2ef51e756ad.tar.xz |
Remove Redfish Node class
Reduces the total number of lines and will allow for easier testing of
the redfish responses.
A main purpose of the node class was to set app.routeDynamic(). However
now app.routeDynamic can handle the complexity that was once in critical
to node. The macro app.routeDynamic() provides a shorter cleaner
interface to the unerlying app.routeDyanic call. The old pattern set
permissions for 6 interfaces (get, head, patch, put, delete_, and post)
even if only one interface is created. That pattern creates unneeded
code that can be safely removed with no effect.
Unit test for the responses would have to mock the node the class in
order to fully test responses.
see https://github.com/openbmc/bmcweb/issues/181
The following files still need node to be extracted.
virtual_media.hpp
account_service.hpp
redfish_sessions.hpp
ethernet.hpp
The files above use a pattern that is not trivial to address. Often their
responses call an async lambda capturing the inherited class. ie
(https://github.com/openbmc/bmcweb/blob/ffed87b5ad1797ca966d030e7f979770
28d258fa/redfish-core/lib/account_service.hpp#L1393)
At a later point I plan to remove node from the files above.
Tested:
I ran the docker unit test with the following command.
WORKSPACE=$(pwd) UNIT_TEST_PKG=bmcweb
./openbmc-build-scripts/run-unit-test-docker.sh
I ran the validator and this change did not create any issues.
python3 RedfishServiceValidator.py -c config.ini
Signed-off-by: John Edward Broadbent <jebr@google.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I147a0289c52cb4198345b1ad9bfe6fdddf57f3df
Diffstat (limited to 'redfish-core/lib/event_service.hpp')
-rw-r--r-- | redfish-core/lib/event_service.hpp | 1105 |
1 files changed, 533 insertions, 572 deletions
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp index 64a20092ff..367b6a2ef8 100644 --- a/redfish-core/lib/event_service.hpp +++ b/redfish-core/lib/event_service.hpp @@ -16,6 +16,7 @@ #pragma once #include "event_service_manager.hpp" +#include <app.hpp> namespace redfish { @@ -36,612 +37,572 @@ static constexpr const std::array<const char*, 1> supportedResourceTypes = { static constexpr const uint8_t maxNoOfSubscriptions = 20; -class EventService : public Node +inline void requestRoutesEventService(App& app) { - public: - EventService(App& app) : Node(app, "/redfish/v1/EventService/") - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request&, const std::vector<std::string>&) override - { - - asyncResp->res.jsonValue = { - {"@odata.type", "#EventService.v1_5_0.EventService"}, - {"Id", "EventService"}, - {"Name", "Event Service"}, - {"Subscriptions", - {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}}, - {"Actions", - {{"#EventService.SubmitTestEvent", - {{"target", "/redfish/v1/EventService/Actions/" - "EventService.SubmitTestEvent"}}}}}, - {"@odata.id", "/redfish/v1/EventService"}}; - - const auto& [enabled, retryAttempts, retryTimeoutInterval] = - EventServiceManager::getInstance().getEventServiceConfig(); - - asyncResp->res.jsonValue["Status"]["State"] = - (enabled ? "Enabled" : "Disabled"); - asyncResp->res.jsonValue["ServiceEnabled"] = enabled; - asyncResp->res.jsonValue["DeliveryRetryAttempts"] = retryAttempts; - asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = - retryTimeoutInterval; - asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes; - asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = supportedResourceTypes; - - nlohmann::json supportedSSEFilters = { - {"EventFormatType", true}, {"MessageId", true}, - {"MetricReportDefinition", true}, {"RegistryPrefix", true}, - {"OriginResource", false}, {"ResourceType", false}}; - - asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = - supportedSSEFilters; - } - - void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request& req, - const std::vector<std::string>&) override - { - - std::optional<bool> serviceEnabled; - std::optional<uint32_t> retryAttemps; - std::optional<uint32_t> retryInterval; - - if (!json_util::readJson(req, asyncResp->res, "ServiceEnabled", - serviceEnabled, "DeliveryRetryAttempts", - retryAttemps, "DeliveryRetryIntervalSeconds", - retryInterval)) - { - return; - } - - auto [enabled, retryCount, retryTimeoutInterval] = - EventServiceManager::getInstance().getEventServiceConfig(); - - if (serviceEnabled) - { - enabled = *serviceEnabled; - } - - if (retryAttemps) - { - // Supported range [1-3] - if ((*retryAttemps < 1) || (*retryAttemps > 3)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryAttemps), - "DeliveryRetryAttempts", "[1-3]"); - } - else - { - retryCount = *retryAttemps; - } - } - - if (retryInterval) - { - // Supported range [30 - 180] - if ((*retryInterval < 30) || (*retryInterval > 180)) - { - messages::queryParameterOutOfRange( - asyncResp->res, std::to_string(*retryInterval), - "DeliveryRetryIntervalSeconds", "[30-180]"); - } - else + BMCWEB_ROUTE(app, "/redfish/v1/EventService/") + .privileges({"Login"}) + .methods(boost::beast::http::verb::get)( + [](const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + asyncResp->res.jsonValue = { + {"@odata.type", "#EventService.v1_5_0.EventService"}, + {"Id", "EventService"}, + {"Name", "Event Service"}, + {"Subscriptions", + {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}}, + {"Actions", + {{"#EventService.SubmitTestEvent", + {{"target", "/redfish/v1/EventService/Actions/" + "EventService.SubmitTestEvent"}}}}}, + {"@odata.id", "/redfish/v1/EventService"}}; + + const auto& [enabled, retryAttempts, retryTimeoutInterval] = + EventServiceManager::getInstance().getEventServiceConfig(); + + asyncResp->res.jsonValue["Status"]["State"] = + (enabled ? "Enabled" : "Disabled"); + asyncResp->res.jsonValue["ServiceEnabled"] = enabled; + asyncResp->res.jsonValue["DeliveryRetryAttempts"] = + retryAttempts; + asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] = + retryTimeoutInterval; + asyncResp->res.jsonValue["EventFormatTypes"] = + supportedEvtFormatTypes; + asyncResp->res.jsonValue["RegistryPrefixes"] = + supportedRegPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = + supportedResourceTypes; + + nlohmann::json supportedSSEFilters = { + {"EventFormatType", true}, {"MessageId", true}, + {"MetricReportDefinition", true}, {"RegistryPrefix", true}, + {"OriginResource", false}, {"ResourceType", false}}; + + asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] = + supportedSSEFilters; + }); + + BMCWEB_ROUTE(app, "/redfish/v1/EventService/") + .privileges({"ConfigureManager"}) + .methods(boost::beast::http::verb::patch)( + [](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) + { - retryTimeoutInterval = *retryInterval; - } - } + std::optional<bool> serviceEnabled; + std::optional<uint32_t> retryAttemps; + std::optional<uint32_t> retryInterval; + + if (!json_util::readJson( + req, asyncResp->res, "ServiceEnabled", serviceEnabled, + "DeliveryRetryAttempts", retryAttemps, + "DeliveryRetryIntervalSeconds", retryInterval)) + { + return; + } + + auto [enabled, retryCount, retryTimeoutInterval] = + EventServiceManager::getInstance().getEventServiceConfig(); + + if (serviceEnabled) + { + enabled = *serviceEnabled; + } + + if (retryAttemps) + { + // Supported range [1-3] + if ((*retryAttemps < 1) || (*retryAttemps > 3)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryAttemps), + "DeliveryRetryAttempts", "[1-3]"); + } + else + { + retryCount = *retryAttemps; + } + } - EventServiceManager::getInstance().setEventServiceConfig( - std::make_tuple(enabled, retryCount, retryTimeoutInterval)); - } -}; + if (retryInterval) + { + // Supported range [30 - 180] + if ((*retryInterval < 30) || (*retryInterval > 180)) + { + messages::queryParameterOutOfRange( + asyncResp->res, std::to_string(*retryInterval), + "DeliveryRetryIntervalSeconds", "[30-180]"); + } + else + { + retryTimeoutInterval = *retryInterval; + } + } + + EventServiceManager::getInstance().setEventServiceConfig( + std::make_tuple(enabled, retryCount, retryTimeoutInterval)); + }); +} -class SubmitTestEvent : public Node +inline void requestRoutesSubmitTestEvent(App& app) { - public: - SubmitTestEvent(App& app) : - Node(app, - "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/") - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request&, const std::vector<std::string>&) override - { - EventServiceManager::getInstance().sendTestEventLog(); - asyncResp->res.result(boost::beast::http::status::no_content); - } -}; - -class EventDestinationCollection : public Node + + BMCWEB_ROUTE( + app, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/") + .privileges({"ConfigureManager"}) + .methods(boost::beast::http::verb::post)( + [](const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + EventServiceManager::getInstance().sendTestEventLog(); + asyncResp->res.result(boost::beast::http::status::no_content); + }); +} + +inline void requestRoutesEventDestinationCollection(App& app) { - public: - EventDestinationCollection(App& app) : - Node(app, "/redfish/v1/EventService/Subscriptions/") - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request&, const std::vector<std::string>&) override - { - - asyncResp->res.jsonValue = { - {"@odata.type", - "#EventDestinationCollection.EventDestinationCollection"}, - {"@odata.id", "/redfish/v1/EventService/Subscriptions"}, - {"Name", "Event Destination Collections"}}; - - nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"]; - - std::vector<std::string> subscripIds = - EventServiceManager::getInstance().getAllIDs(); - memberArray = nlohmann::json::array(); - asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size(); - - for (const std::string& id : subscripIds) - { - memberArray.push_back( - {{"@odata.id", - "/redfish/v1/EventService/Subscriptions/" + id}}); - } - } - - void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request& req, - const std::vector<std::string>&) override - { - - if (EventServiceManager::getInstance().getNumberOfSubscriptions() >= - maxNoOfSubscriptions) - { - messages::eventSubscriptionLimitExceeded(asyncResp->res); - return; - } - std::string destUrl; - std::string protocol; - std::optional<std::string> context; - std::optional<std::string> subscriptionType; - std::optional<std::string> eventFormatType2; - std::optional<std::string> retryPolicy; - std::optional<std::vector<std::string>> msgIds; - std::optional<std::vector<std::string>> regPrefixes; - std::optional<std::vector<std::string>> resTypes; - std::optional<std::vector<nlohmann::json>> headers; - std::optional<std::vector<nlohmann::json>> mrdJsonArray; - - if (!json_util::readJson( - req, asyncResp->res, "Destination", destUrl, "Context", context, - "Protocol", protocol, "SubscriptionType", subscriptionType, - "EventFormatType", eventFormatType2, "HttpHeaders", headers, - "RegistryPrefixes", regPrefixes, "MessageIds", msgIds, - "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions", - mrdJsonArray, "ResourceTypes", resTypes)) - { - return; - } - - if (regPrefixes && msgIds) - { - if (regPrefixes->size() && msgIds->size()) - { - messages::mutualExclusiveProperties( - asyncResp->res, "RegistryPrefixes", "MessageIds"); - return; - } - } - - // Validate the URL using regex expression - // Format: <protocol>://<host>:<port>/<uri> - // protocol: http/https - // host: Exclude ' ', ':', '#', '?' - // port: Empty or numeric value with ':' separator. - // uri: Start with '/' and Exclude '#', ' ' - // Can include query params(ex: '/event?test=1') - // TODO: Need to validate hostname extensively(as per rfc) - const std::regex urlRegex( - "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/" - "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)"); - std::cmatch match; - if (!std::regex_match(destUrl.c_str(), match, urlRegex)) - { - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; - } - - std::string uriProto = std::string(match[1].first, match[1].second); - if (uriProto == "http") - { -#ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING - messages::propertyValueFormatError(asyncResp->res, destUrl, - "Destination"); - return; -#endif - } - - std::string host = std::string(match[2].first, match[2].second); - std::string port = std::string(match[3].first, match[3].second); - std::string path = std::string(match[4].first, match[4].second); - if (port.empty()) - { - if (uriProto == "http") - { - port = "80"; - } - else - { - port = "443"; - } - } - if (path.empty()) - { - path = "/"; - } - - std::shared_ptr<Subscription> subValue = - std::make_shared<Subscription>(host, port, path, uriProto); - - subValue->destinationUrl = destUrl; - - if (subscriptionType) - { - if (*subscriptionType != "RedfishEvent") - { - messages::propertyValueNotInList( - asyncResp->res, *subscriptionType, "SubscriptionType"); - return; - } - subValue->subscriptionType = *subscriptionType; - } - else - { - subValue->subscriptionType = "RedfishEvent"; // Default - } - - if (protocol != "Redfish") - { - messages::propertyValueNotInList(asyncResp->res, protocol, - "Protocol"); - return; - } - subValue->protocol = protocol; - - if (eventFormatType2) - { - if (std::find(supportedEvtFormatTypes.begin(), - supportedEvtFormatTypes.end(), - *eventFormatType2) == supportedEvtFormatTypes.end()) - { - messages::propertyValueNotInList( - asyncResp->res, *eventFormatType2, "EventFormatType"); - return; - } - subValue->eventFormatType = *eventFormatType2; - } - else - { - // If not specified, use default "Event" - subValue->eventFormatType = "Event"; - } - - if (context) - { - subValue->customText = *context; - } - - if (headers) - { - subValue->httpHeaders = *headers; - } - - if (regPrefixes) - { - for (const std::string& it : *regPrefixes) - { - if (std::find(supportedRegPrefixes.begin(), - supportedRegPrefixes.end(), - it) == supportedRegPrefixes.end()) + BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions") + .privileges({"Login"}) + .methods(boost::beast::http::verb::get)( + [](const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + asyncResp->res.jsonValue = { + {"@odata.type", + "#EventDestinationCollection.EventDestinationCollection"}, + {"@odata.id", "/redfish/v1/EventService/Subscriptions"}, + {"Name", "Event Destination Collections"}}; + + nlohmann::json& memberArray = + asyncResp->res.jsonValue["Members"]; + + std::vector<std::string> subscripIds = + EventServiceManager::getInstance().getAllIDs(); + memberArray = nlohmann::json::array(); + asyncResp->res.jsonValue["Members@odata.count"] = + subscripIds.size(); + + for (const std::string& id : subscripIds) + { + memberArray.push_back( + {{"@odata.id", + "/redfish/v1/EventService/Subscriptions/" + id}}); + } + }); + BMCWEB_ROUTE(app, "/redfish/v1/EventService/Subscriptions/") + .privileges({"ConfigureManager"}) + .methods(boost::beast::http::verb::post)( + [](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { + if (EventServiceManager::getInstance() + .getNumberOfSubscriptions() >= maxNoOfSubscriptions) + { + messages::eventSubscriptionLimitExceeded(asyncResp->res); + return; + } + std::string destUrl; + std::string protocol; + std::optional<std::string> context; + std::optional<std::string> subscriptionType; + std::optional<std::string> eventFormatType2; + std::optional<std::string> retryPolicy; + std::optional<std::vector<std::string>> msgIds; + std::optional<std::vector<std::string>> regPrefixes; + std::optional<std::vector<std::string>> resTypes; + std::optional<std::vector<nlohmann::json>> headers; + std::optional<std::vector<nlohmann::json>> mrdJsonArray; + + if (!json_util::readJson( + req, asyncResp->res, "Destination", destUrl, "Context", + context, "Protocol", protocol, "SubscriptionType", + subscriptionType, "EventFormatType", eventFormatType2, + "HttpHeaders", headers, "RegistryPrefixes", regPrefixes, + "MessageIds", msgIds, "DeliveryRetryPolicy", + retryPolicy, "MetricReportDefinitions", mrdJsonArray, + "ResourceTypes", resTypes)) { - messages::propertyValueNotInList(asyncResp->res, it, - "RegistryPrefixes"); return; } - } - subValue->registryPrefixes = *regPrefixes; - } - if (resTypes) - { - for (const std::string& it : *resTypes) - { - if (std::find(supportedResourceTypes.begin(), - supportedResourceTypes.end(), - it) == supportedResourceTypes.end()) + if (regPrefixes && msgIds) + { + if (regPrefixes->size() && msgIds->size()) + { + messages::mutualExclusiveProperties( + asyncResp->res, "RegistryPrefixes", "MessageIds"); + return; + } + } + + // Validate the URL using regex expression + // Format: <protocol>://<host>:<port>/<uri> + // protocol: http/https + // host: Exclude ' ', ':', '#', '?' + // port: Empty or numeric value with ':' separator. + // uri: Start with '/' and Exclude '#', ' ' + // Can include query params(ex: '/event?test=1') + // TODO: Need to validate hostname extensively(as per rfc) + const std::regex urlRegex( + "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/" + "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)"); + std::cmatch match; + if (!std::regex_match(destUrl.c_str(), match, urlRegex)) { - messages::propertyValueNotInList(asyncResp->res, it, - "ResourceTypes"); + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); return; } - } - subValue->resourceTypes = *resTypes; - } - if (msgIds) - { - std::vector<std::string> registryPrefix; + std::string uriProto = + std::string(match[1].first, match[1].second); + if (uriProto == "http") + { +#ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING + messages::propertyValueFormatError(asyncResp->res, destUrl, + "Destination"); + return; +#endif + } - // If no registry prefixes are mentioned, consider all supported - // prefixes - if (subValue->registryPrefixes.empty()) - { - registryPrefix.assign(supportedRegPrefixes.begin(), - supportedRegPrefixes.end()); - } - else - { - registryPrefix = subValue->registryPrefixes; - } + std::string host = std::string(match[2].first, match[2].second); + std::string port = std::string(match[3].first, match[3].second); + std::string path = std::string(match[4].first, match[4].second); + if (port.empty()) + { + if (uriProto == "http") + { + port = "80"; + } + else + { + port = "443"; + } + } + if (path.empty()) + { + path = "/"; + } - for (const std::string& id : *msgIds) - { - bool validId = false; - - // Check for Message ID in each of the selected Registry - for (const std::string& it : registryPrefix) - { - const boost::beast::span< - const redfish::message_registries::MessageEntry> - registry = - redfish::message_registries::getRegistryFromPrefix( - it); - - if (std::any_of( - registry.cbegin(), registry.cend(), - [&id]( - const redfish::message_registries::MessageEntry& - messageEntry) { - return !id.compare(messageEntry.first); - })) + std::shared_ptr<Subscription> subValue = + std::make_shared<Subscription>(host, port, path, uriProto); + + subValue->destinationUrl = destUrl; + + if (subscriptionType) + { + if (*subscriptionType != "RedfishEvent") { - validId = true; - break; + messages::propertyValueNotInList(asyncResp->res, + *subscriptionType, + "SubscriptionType"); + return; } + subValue->subscriptionType = *subscriptionType; + } + else + { + subValue->subscriptionType = "RedfishEvent"; // Default } - if (!validId) + if (protocol != "Redfish") { - messages::propertyValueNotInList(asyncResp->res, id, - "MessageIds"); + messages::propertyValueNotInList(asyncResp->res, protocol, + "Protocol"); return; } - } + subValue->protocol = protocol; + + if (eventFormatType2) + { + if (std::find(supportedEvtFormatTypes.begin(), + supportedEvtFormatTypes.end(), + *eventFormatType2) == + supportedEvtFormatTypes.end()) + { + messages::propertyValueNotInList(asyncResp->res, + *eventFormatType2, + "EventFormatType"); + return; + } + subValue->eventFormatType = *eventFormatType2; + } + else + { + // If not specified, use default "Event" + subValue->eventFormatType = "Event"; + } - subValue->registryMsgIds = *msgIds; - } + if (context) + { + subValue->customText = *context; + } - if (retryPolicy) - { - if (std::find(supportedRetryPolicies.begin(), - supportedRetryPolicies.end(), - *retryPolicy) == supportedRetryPolicies.end()) - { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; - } - subValue->retryPolicy = *retryPolicy; - } - else - { - // Default "TerminateAfterRetries" - subValue->retryPolicy = "TerminateAfterRetries"; - } - - if (mrdJsonArray) - { - for (nlohmann::json& mrdObj : *mrdJsonArray) - { - std::string mrdUri; - if (json_util::getValueFromJsonObject(mrdObj, "@odata.id", - mrdUri)) + if (headers) + { + subValue->httpHeaders = *headers; + } + + if (regPrefixes) + { + for (const std::string& it : *regPrefixes) + { + if (std::find(supportedRegPrefixes.begin(), + supportedRegPrefixes.end(), + it) == supportedRegPrefixes.end()) + { + messages::propertyValueNotInList( + asyncResp->res, it, "RegistryPrefixes"); + return; + } + } + subValue->registryPrefixes = *regPrefixes; + } + + if (resTypes) + { + for (const std::string& it : *resTypes) + { + if (std::find(supportedResourceTypes.begin(), + supportedResourceTypes.end(), + it) == supportedResourceTypes.end()) + { + messages::propertyValueNotInList(asyncResp->res, it, + "ResourceTypes"); + return; + } + } + subValue->resourceTypes = *resTypes; + } + + if (msgIds) + { + std::vector<std::string> registryPrefix; + + // If no registry prefixes are mentioned, consider all + // supported prefixes + if (subValue->registryPrefixes.empty()) + { + registryPrefix.assign(supportedRegPrefixes.begin(), + supportedRegPrefixes.end()); + } + else + { + registryPrefix = subValue->registryPrefixes; + } + + for (const std::string& id : *msgIds) + { + bool validId = false; + + // Check for Message ID in each of the selected Registry + for (const std::string& it : registryPrefix) + { + const boost::beast::span< + const redfish::message_registries::MessageEntry> + registry = redfish::message_registries:: + getRegistryFromPrefix(it); + + if (std::any_of( + registry.cbegin(), registry.cend(), + [&id](const redfish::message_registries:: + MessageEntry& messageEntry) { + return !id.compare(messageEntry.first); + })) + { + validId = true; + break; + } + } + + if (!validId) + { + messages::propertyValueNotInList(asyncResp->res, id, + "MessageIds"); + return; + } + } + + subValue->registryMsgIds = *msgIds; + } + + if (retryPolicy) { - subValue->metricReportDefinitions.emplace_back(mrdUri); + if (std::find(supportedRetryPolicies.begin(), + supportedRetryPolicies.end(), + *retryPolicy) == supportedRetryPolicies.end()) + { + messages::propertyValueNotInList(asyncResp->res, + *retryPolicy, + "DeliveryRetryPolicy"); + return; + } + subValue->retryPolicy = *retryPolicy; } else { - messages::propertyValueFormatError( - asyncResp->res, - mrdObj.dump(2, ' ', true, + // Default "TerminateAfterRetries" + subValue->retryPolicy = "TerminateAfterRetries"; + } + + if (mrdJsonArray) + { + for (nlohmann::json& mrdObj : *mrdJsonArray) + { + std::string mrdUri; + if (json_util::getValueFromJsonObject( + mrdObj, "@odata.id", mrdUri)) + { + subValue->metricReportDefinitions.emplace_back( + mrdUri); + } + else + { + messages::propertyValueFormatError( + asyncResp->res, + mrdObj.dump( + 2, ' ', true, nlohmann::json::error_handler_t::replace), - "MetricReportDefinitions"); - return; + "MetricReportDefinitions"); + return; + } + } } - } - } - std::string id = - EventServiceManager::getInstance().addSubscription(subValue); - if (id.empty()) - { - messages::internalError(asyncResp->res); - return; - } + std::string id = + EventServiceManager::getInstance().addSubscription( + subValue); + if (id.empty()) + { + messages::internalError(asyncResp->res); + return; + } - messages::created(asyncResp->res); - asyncResp->res.addHeader( - "Location", "/redfish/v1/EventService/Subscriptions/" + id); - } -}; + messages::created(asyncResp->res); + asyncResp->res.addHeader( + "Location", "/redfish/v1/EventService/Subscriptions/" + id); + }); +} -class EventDestination : public Node +inline void requestRoutesEventDestination(App& app) { - public: - EventDestination(App& app) : - Node(app, "/redfish/v1/EventService/Subscriptions/<str>/", - std::string()) - { - entityPrivileges = { - {boost::beast::http::verb::get, {{"Login"}}}, - {boost::beast::http::verb::head, {{"Login"}}}, - {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, - {boost::beast::http::verb::put, {{"ConfigureManager"}}}, - {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, - {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; - } - - private: - void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request&, - const std::vector<std::string>& params) override - { - - if (params.size() != 1) - { - messages::internalError(asyncResp->res); - return; - } - - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(params[0]); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - const std::string& id = params[0]; - - asyncResp->res.jsonValue = { - {"@odata.type", "#EventDestination.v1_7_0.EventDestination"}, - {"Protocol", "Redfish"}}; - asyncResp->res.jsonValue["@odata.id"] = - "/redfish/v1/EventService/Subscriptions/" + id; - asyncResp->res.jsonValue["Id"] = id; - asyncResp->res.jsonValue["Name"] = "Event Destination " + id; - asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl; - asyncResp->res.jsonValue["Context"] = subValue->customText; - asyncResp->res.jsonValue["SubscriptionType"] = - subValue->subscriptionType; - asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders; - asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType; - asyncResp->res.jsonValue["RegistryPrefixes"] = - subValue->registryPrefixes; - asyncResp->res.jsonValue["ResourceTypes"] = subValue->resourceTypes; - - asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds; - asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy; - - std::vector<nlohmann::json> mrdJsonArray; - for (const auto& mdrUri : subValue->metricReportDefinitions) - { - mrdJsonArray.push_back({{"@odata.id", mdrUri}}); - } - asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray; - } - - void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request& req, - const std::vector<std::string>& params) override - { - - if (params.size() != 1) - { - messages::internalError(asyncResp->res); - return; - } - - std::shared_ptr<Subscription> subValue = - EventServiceManager::getInstance().getSubscription(params[0]); - if (subValue == nullptr) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - - std::optional<std::string> context; - std::optional<std::string> retryPolicy; - std::optional<std::vector<nlohmann::json>> headers; - - if (!json_util::readJson(req, asyncResp->res, "Context", context, - "DeliveryRetryPolicy", retryPolicy, - "HttpHeaders", headers)) - { - return; - } - - if (context) - { - subValue->customText = *context; - } - - if (headers) - { - subValue->httpHeaders = *headers; - } - - if (retryPolicy) - { - if (std::find(supportedRetryPolicies.begin(), - supportedRetryPolicies.end(), - *retryPolicy) == supportedRetryPolicies.end()) - { - messages::propertyValueNotInList(asyncResp->res, *retryPolicy, - "DeliveryRetryPolicy"); - return; - } - subValue->retryPolicy = *retryPolicy; - subValue->updateRetryPolicy(); - } - - EventServiceManager::getInstance().updateSubscriptionData(); - } - - void doDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, - const crow::Request&, - const std::vector<std::string>& params) override - { - - if (params.size() != 1) - { - messages::internalError(asyncResp->res); - return; - } - - if (!EventServiceManager::getInstance().isSubscriptionExist(params[0])) - { - asyncResp->res.result(boost::beast::http::status::not_found); - return; - } - EventServiceManager::getInstance().deleteSubscription(params[0]); - } -}; + BMCWEB_ROUTE(app, "redfish/v1/EventService/Subscriptions/<str>/") + .privileges({"Login"}) + .methods(boost::beast::http::verb::get)( + [](const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& param) { + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + const std::string& id = param; + + asyncResp->res.jsonValue = { + {"@odata.type", + "#EventDestination.v1_7_0.EventDestination"}, + {"Protocol", "Redfish"}}; + asyncResp->res.jsonValue["@odata.id"] = + "/redfish/v1/EventService/Subscriptions/" + id; + asyncResp->res.jsonValue["Id"] = id; + asyncResp->res.jsonValue["Name"] = "Event Destination " + id; + asyncResp->res.jsonValue["Destination"] = + subValue->destinationUrl; + asyncResp->res.jsonValue["Context"] = subValue->customText; + asyncResp->res.jsonValue["SubscriptionType"] = + subValue->subscriptionType; + asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders; + asyncResp->res.jsonValue["EventFormatType"] = + subValue->eventFormatType; + asyncResp->res.jsonValue["RegistryPrefixes"] = + subValue->registryPrefixes; + asyncResp->res.jsonValue["ResourceTypes"] = + subValue->resourceTypes; + + asyncResp->res.jsonValue["MessageIds"] = + subValue->registryMsgIds; + asyncResp->res.jsonValue["DeliveryRetryPolicy"] = + subValue->retryPolicy; + + std::vector<nlohmann::json> mrdJsonArray; + for (const auto& mdrUri : subValue->metricReportDefinitions) + { + mrdJsonArray.push_back({{"@odata.id", mdrUri}}); + } + asyncResp->res.jsonValue["MetricReportDefinitions"] = + mrdJsonArray; + }); + /////redfish/v1/EventService/Subscriptions/ + // ConfigureManager + BMCWEB_ROUTE(app, "redfish/v1/EventService/Subscriptions/<str>/") + .privileges({"ConfigureManager"}) + .methods(boost::beast::http::verb::patch)( + [](const crow::Request& req, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& param) { + std::shared_ptr<Subscription> subValue = + EventServiceManager::getInstance().getSubscription(param); + if (subValue == nullptr) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + + std::optional<std::string> context; + std::optional<std::string> retryPolicy; + std::optional<std::vector<nlohmann::json>> headers; + + if (!json_util::readJson(req, asyncResp->res, "Context", + context, "DeliveryRetryPolicy", + retryPolicy, "HttpHeaders", headers)) + { + return; + } + + if (context) + { + subValue->customText = *context; + } + + if (headers) + { + subValue->httpHeaders = *headers; + } + + if (retryPolicy) + { + if (std::find(supportedRetryPolicies.begin(), + supportedRetryPolicies.end(), + *retryPolicy) == supportedRetryPolicies.end()) + { + messages::propertyValueNotInList(asyncResp->res, + *retryPolicy, + "DeliveryRetryPolicy"); + return; + } + subValue->retryPolicy = *retryPolicy; + subValue->updateRetryPolicy(); + } + + EventServiceManager::getInstance().updateSubscriptionData(); + }); + BMCWEB_ROUTE(app, "redfish/v1/EventService/Subscriptions/<str>/") + .privileges({"ConfigureManager"}) + .methods(boost::beast::http::verb::delete_)( + [](const crow::Request&, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& param) { + if (!EventServiceManager::getInstance().isSubscriptionExist( + param)) + { + asyncResp->res.result( + boost::beast::http::status::not_found); + return; + } + EventServiceManager::getInstance().deleteSubscription(param); + }); +} } // namespace redfish |