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
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index 64a2009..367b6a2 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 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"}}}};
-    }
+    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"}};
 
-  private:
-    void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-               const crow::Request&, const std::vector<std::string>&) override
-    {
+                const auto& [enabled, retryAttempts, retryTimeoutInterval] =
+                    EventServiceManager::getInstance().getEventServiceConfig();
 
-        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"}};
+                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;
 
-        const auto& [enabled, retryAttempts, retryTimeoutInterval] =
-            EventServiceManager::getInstance().getEventServiceConfig();
+                nlohmann::json supportedSSEFilters = {
+                    {"EventFormatType", true},        {"MessageId", true},
+                    {"MetricReportDefinition", true}, {"RegistryPrefix", true},
+                    {"OriginResource", false},        {"ResourceType", false}};
 
-        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;
+                asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
+                    supportedSSEFilters;
+            });
 
-        nlohmann::json supportedSSEFilters = {
-            {"EventFormatType", true},        {"MessageId", true},
-            {"MetricReportDefinition", true}, {"RegistryPrefix", true},
-            {"OriginResource", false},        {"ResourceType", false}};
+    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)
 
-        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;
-            }
-        }
+                std::optional<bool> serviceEnabled;
+                std::optional<uint32_t> retryAttemps;
+                std::optional<uint32_t> retryInterval;
 
-        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
-{
-  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
-{
-  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())
+                if (!json_util::readJson(
+                        req, asyncResp->res, "ServiceEnabled", serviceEnabled,
+                        "DeliveryRetryAttempts", retryAttemps,
+                        "DeliveryRetryIntervalSeconds", retryInterval))
                 {
-                    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())
+                auto [enabled, retryCount, retryTimeoutInterval] =
+                    EventServiceManager::getInstance().getEventServiceConfig();
+
+                if (serviceEnabled)
                 {
-                    messages::propertyValueNotInList(asyncResp->res, it,
-                                                     "ResourceTypes");
-                    return;
+                    enabled = *serviceEnabled;
                 }
-            }
-            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)
+                if (retryAttemps)
                 {
-                    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);
-                            }))
+                    // Supported range [1-3]
+                    if ((*retryAttemps < 1) || (*retryAttemps > 3))
                     {
-                        validId = true;
-                        break;
+                        messages::queryParameterOutOfRange(
+                            asyncResp->res, std::to_string(*retryAttemps),
+                            "DeliveryRetryAttempts", "[1-3]");
+                    }
+                    else
+                    {
+                        retryCount = *retryAttemps;
                     }
                 }
 
-                if (!validId)
+                if (retryInterval)
                 {
-                    messages::propertyValueNotInList(asyncResp->res, id,
-                                                     "MessageIds");
+                    // 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));
+            });
+}
+
+inline void requestRoutesSubmitTestEvent(App& app)
+{
+
+    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)
+{
+    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;
 
-            subValue->registryMsgIds = *msgIds;
-        }
-
-        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 (!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))
                 {
-                    subValue->metricReportDefinitions.emplace_back(mrdUri);
+                    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
                 {
-                    messages::propertyValueFormatError(
-                        asyncResp->res,
-                        mrdObj.dump(2, ' ', true,
-                                    nlohmann::json::error_handler_t::replace),
-                        "MetricReportDefinitions");
+                    subValue->subscriptionType = "RedfishEvent"; // Default
+                }
+
+                if (protocol != "Redfish")
+                {
+                    messages::propertyValueNotInList(asyncResp->res, protocol,
+                                                     "Protocol");
                     return;
                 }
-            }
-        }
+                subValue->protocol = protocol;
 
-        std::string id =
-            EventServiceManager::getInstance().addSubscription(subValue);
-        if (id.empty())
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
+                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";
+                }
 
-        messages::created(asyncResp->res);
-        asyncResp->res.addHeader(
-            "Location", "/redfish/v1/EventService/Subscriptions/" + id);
-    }
-};
+                if (context)
+                {
+                    subValue->customText = *context;
+                }
 
-class EventDestination : public Node
+                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)
+                {
+                    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))
+                        {
+                            subValue->metricReportDefinitions.emplace_back(
+                                mrdUri);
+                        }
+                        else
+                        {
+                            messages::propertyValueFormatError(
+                                asyncResp->res,
+                                mrdObj.dump(
+                                    2, ' ', true,
+                                    nlohmann::json::error_handler_t::replace),
+                                "MetricReportDefinitions");
+                            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);
+            });
+}
+
+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"}}}};
-    }
+    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;
 
-  private:
-    void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-               const crow::Request&,
-               const std::vector<std::string>& params) override
-    {
+                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;
 
-        if (params.size() != 1)
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
+                asyncResp->res.jsonValue["MessageIds"] =
+                    subValue->registryMsgIds;
+                asyncResp->res.jsonValue["DeliveryRetryPolicy"] =
+                    subValue->retryPolicy;
 
-        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];
+                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;
+                }
 
-        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;
+                std::optional<std::string> context;
+                std::optional<std::string> retryPolicy;
+                std::optional<std::vector<nlohmann::json>> headers;
 
-        asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
-        asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
+                if (!json_util::readJson(req, asyncResp->res, "Context",
+                                         context, "DeliveryRetryPolicy",
+                                         retryPolicy, "HttpHeaders", headers))
+                {
+                    return;
+                }
 
-        std::vector<nlohmann::json> mrdJsonArray;
-        for (const auto& mdrUri : subValue->metricReportDefinitions)
-        {
-            mrdJsonArray.push_back({{"@odata.id", mdrUri}});
-        }
-        asyncResp->res.jsonValue["MetricReportDefinitions"] = mrdJsonArray;
-    }
+                if (context)
+                {
+                    subValue->customText = *context;
+                }
 
-    void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-                 const crow::Request& req,
-                 const std::vector<std::string>& params) override
-    {
+                if (headers)
+                {
+                    subValue->httpHeaders = *headers;
+                }
 
-        if (params.size() != 1)
-        {
-            messages::internalError(asyncResp->res);
-            return;
-        }
+                if (retryPolicy)
+                {
+                    if (std::find(supportedRetryPolicies.begin(),
+                                  supportedRetryPolicies.end(),
+                                  *retryPolicy) == supportedRetryPolicies.end())
+                    {
+                        messages::propertyValueNotInList(asyncResp->res,
+                                                         *retryPolicy,
+                                                         "DeliveryRetryPolicy");
+                        return;
+                    }
+                    subValue->retryPolicy = *retryPolicy;
+                    subValue->updateRetryPolicy();
+                }
 
-        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]);
-    }
-};
+                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