xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 07941a881d1d07fd8cbe2ac0db88b3c96deab4c7)
1e5aaf047SAppaRao Puli /*
2e5aaf047SAppaRao Puli // Copyright (c) 2020 Intel Corporation
3e5aaf047SAppaRao Puli //
4e5aaf047SAppaRao Puli // Licensed under the Apache License, Version 2.0 (the "License");
5e5aaf047SAppaRao Puli // you may not use this file except in compliance with the License.
6e5aaf047SAppaRao Puli // You may obtain a copy of the License at
7e5aaf047SAppaRao Puli //
8e5aaf047SAppaRao Puli //      http://www.apache.org/licenses/LICENSE-2.0
9e5aaf047SAppaRao Puli //
10e5aaf047SAppaRao Puli // Unless required by applicable law or agreed to in writing, software
11e5aaf047SAppaRao Puli // distributed under the License is distributed on an "AS IS" BASIS,
12e5aaf047SAppaRao Puli // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e5aaf047SAppaRao Puli // See the License for the specific language governing permissions and
14e5aaf047SAppaRao Puli // limitations under the License.
15e5aaf047SAppaRao Puli */
16e5aaf047SAppaRao Puli #pragma once
17b52664e2SAppaRao Puli #include "event_service_manager.hpp"
18e5aaf047SAppaRao Puli 
19e5aaf047SAppaRao Puli namespace redfish
20e5aaf047SAppaRao Puli {
21e5aaf047SAppaRao Puli 
22156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
23156d6b00SAppaRao Puli     eventFormatType, metricReportFormatType};
24e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRegPrefixes = {
25e5aaf047SAppaRao Puli     "Base", "OpenBMC", "Task"};
26e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 3> supportedRetryPolicies = {
27e5aaf047SAppaRao Puli     "TerminateAfterRetries", "SuspendRetries", "RetryForever"};
28e5aaf047SAppaRao Puli 
29e5aaf047SAppaRao Puli static constexpr const uint8_t maxNoOfSubscriptions = 20;
30e5aaf047SAppaRao Puli 
31e5aaf047SAppaRao Puli class EventService : public Node
32e5aaf047SAppaRao Puli {
33e5aaf047SAppaRao Puli   public:
34e5aaf047SAppaRao Puli     EventService(CrowApp& app) : Node(app, "/redfish/v1/EventService/")
35e5aaf047SAppaRao Puli     {
36e5aaf047SAppaRao Puli         entityPrivileges = {
37e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
38e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
39e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
40e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
41e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
42e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
43e5aaf047SAppaRao Puli     }
44e5aaf047SAppaRao Puli 
45e5aaf047SAppaRao Puli   private:
46e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
47e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
48e5aaf047SAppaRao Puli     {
49e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
50e5aaf047SAppaRao Puli         res.jsonValue = {
51e5aaf047SAppaRao Puli             {"@odata.type", "#EventService.v1_5_0.EventService"},
52e5aaf047SAppaRao Puli             {"Id", "EventService"},
53e5aaf047SAppaRao Puli             {"Name", "Event Service"},
54e5aaf047SAppaRao Puli             {"ServerSentEventUri",
55e5aaf047SAppaRao Puli              "/redfish/v1/EventService/Subscriptions/SSE"},
56e5aaf047SAppaRao Puli             {"Subscriptions",
57e5aaf047SAppaRao Puli              {{"@odata.id", "/redfish/v1/EventService/Subscriptions"}}},
580b4bdd93SAppaRao Puli             {"Actions",
590b4bdd93SAppaRao Puli              {{"#EventService.SubmitTestEvent",
600b4bdd93SAppaRao Puli                {{"target", "/redfish/v1/EventService/Actions/"
610b4bdd93SAppaRao Puli                            "EventService.SubmitTestEvent"}}}}},
62e5aaf047SAppaRao Puli             {"@odata.id", "/redfish/v1/EventService"}};
63e5aaf047SAppaRao Puli 
647d1cc387SAppaRao Puli         const auto& [enabled, retryAttempts, retryTimeoutInterval] =
657d1cc387SAppaRao Puli             EventServiceManager::getInstance().getEventServiceConfig();
667d1cc387SAppaRao Puli 
677d1cc387SAppaRao Puli         asyncResp->res.jsonValue["Status"]["State"] =
687d1cc387SAppaRao Puli             (enabled ? "Enabled" : "Disabled");
697d1cc387SAppaRao Puli         asyncResp->res.jsonValue["ServiceEnabled"] = enabled;
707d1cc387SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryAttempts"] = retryAttempts;
71e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
727d1cc387SAppaRao Puli             retryTimeoutInterval;
73e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
74e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
75*07941a88SAyushi Smriti 
76*07941a88SAyushi Smriti         nlohmann::json supportedSSEFilters = {
77*07941a88SAyushi Smriti             {"EventFormatType", true},        {"MessageId", true},
78*07941a88SAyushi Smriti             {"MetricReportDefinition", true}, {"RegistryPrefix", true},
79*07941a88SAyushi Smriti             {"OriginResource", false},        {"ResourceType", false}};
80*07941a88SAyushi Smriti 
81*07941a88SAyushi Smriti         asyncResp->res.jsonValue["SSEFilterPropertiesSupported"] =
82*07941a88SAyushi Smriti             supportedSSEFilters;
83e5aaf047SAppaRao Puli     }
84e5aaf047SAppaRao Puli 
85e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
86e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
87e5aaf047SAppaRao Puli     {
88e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
89e5aaf047SAppaRao Puli 
90e5aaf047SAppaRao Puli         std::optional<bool> serviceEnabled;
91e5aaf047SAppaRao Puli         std::optional<uint32_t> retryAttemps;
92e5aaf047SAppaRao Puli         std::optional<uint32_t> retryInterval;
93e5aaf047SAppaRao Puli 
94e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "ServiceEnabled", serviceEnabled,
95e5aaf047SAppaRao Puli                                  "DeliveryRetryAttempts", retryAttemps,
96e5aaf047SAppaRao Puli                                  "DeliveryRetryIntervalSeconds", retryInterval))
97e5aaf047SAppaRao Puli         {
98e5aaf047SAppaRao Puli             return;
99e5aaf047SAppaRao Puli         }
100e5aaf047SAppaRao Puli 
1017d1cc387SAppaRao Puli         auto [enabled, retryCount, retryTimeoutInterval] =
1027d1cc387SAppaRao Puli             EventServiceManager::getInstance().getEventServiceConfig();
1037d1cc387SAppaRao Puli 
104e5aaf047SAppaRao Puli         if (serviceEnabled)
105e5aaf047SAppaRao Puli         {
1067d1cc387SAppaRao Puli             enabled = *serviceEnabled;
107e5aaf047SAppaRao Puli         }
108e5aaf047SAppaRao Puli 
109e5aaf047SAppaRao Puli         if (retryAttemps)
110e5aaf047SAppaRao Puli         {
111e5aaf047SAppaRao Puli             // Supported range [1-3]
112e5aaf047SAppaRao Puli             if ((*retryAttemps < 1) || (*retryAttemps > 3))
113e5aaf047SAppaRao Puli             {
114e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
115e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryAttemps),
116e5aaf047SAppaRao Puli                     "DeliveryRetryAttempts", "[1-3]");
117e5aaf047SAppaRao Puli             }
118e5aaf047SAppaRao Puli             else
119e5aaf047SAppaRao Puli             {
1207d1cc387SAppaRao Puli                 retryCount = *retryAttemps;
121e5aaf047SAppaRao Puli             }
122e5aaf047SAppaRao Puli         }
123e5aaf047SAppaRao Puli 
124e5aaf047SAppaRao Puli         if (retryInterval)
125e5aaf047SAppaRao Puli         {
126e5aaf047SAppaRao Puli             // Supported range [30 - 180]
127e5aaf047SAppaRao Puli             if ((*retryInterval < 30) || (*retryInterval > 180))
128e5aaf047SAppaRao Puli             {
129e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
130e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryInterval),
131e5aaf047SAppaRao Puli                     "DeliveryRetryIntervalSeconds", "[30-180]");
132e5aaf047SAppaRao Puli             }
133e5aaf047SAppaRao Puli             else
134e5aaf047SAppaRao Puli             {
1357d1cc387SAppaRao Puli                 retryTimeoutInterval = *retryInterval;
136e5aaf047SAppaRao Puli             }
137e5aaf047SAppaRao Puli         }
138e5aaf047SAppaRao Puli 
1397d1cc387SAppaRao Puli         EventServiceManager::getInstance().setEventServiceConfig(
1407d1cc387SAppaRao Puli             std::make_tuple(enabled, retryCount, retryTimeoutInterval));
141e5aaf047SAppaRao Puli     }
142e5aaf047SAppaRao Puli };
143e5aaf047SAppaRao Puli 
1440b4bdd93SAppaRao Puli class SubmitTestEvent : public Node
1450b4bdd93SAppaRao Puli {
1460b4bdd93SAppaRao Puli   public:
1470b4bdd93SAppaRao Puli     SubmitTestEvent(CrowApp& app) :
1480b4bdd93SAppaRao Puli         Node(app,
1490b4bdd93SAppaRao Puli              "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
1500b4bdd93SAppaRao Puli     {
1510b4bdd93SAppaRao Puli         entityPrivileges = {
1520b4bdd93SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
1530b4bdd93SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
1540b4bdd93SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1550b4bdd93SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1560b4bdd93SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1570b4bdd93SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1580b4bdd93SAppaRao Puli     }
1590b4bdd93SAppaRao Puli 
1600b4bdd93SAppaRao Puli   private:
1610b4bdd93SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
1620b4bdd93SAppaRao Puli                 const std::vector<std::string>& params) override
1630b4bdd93SAppaRao Puli     {
1640b4bdd93SAppaRao Puli         EventServiceManager::getInstance().sendTestEventLog();
1650b4bdd93SAppaRao Puli         res.result(boost::beast::http::status::no_content);
1660b4bdd93SAppaRao Puli         res.end();
1670b4bdd93SAppaRao Puli     }
1680b4bdd93SAppaRao Puli };
1690b4bdd93SAppaRao Puli 
170e5aaf047SAppaRao Puli class EventDestinationCollection : public Node
171e5aaf047SAppaRao Puli {
172e5aaf047SAppaRao Puli   public:
173e5aaf047SAppaRao Puli     EventDestinationCollection(CrowApp& app) :
174e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/")
175e5aaf047SAppaRao Puli     {
176e5aaf047SAppaRao Puli         entityPrivileges = {
177e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
178e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
179e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
180e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
181e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
182e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
183e5aaf047SAppaRao Puli     }
184e5aaf047SAppaRao Puli 
185e5aaf047SAppaRao Puli   private:
186e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
187e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
188e5aaf047SAppaRao Puli     {
189e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
190e5aaf047SAppaRao Puli 
191e5aaf047SAppaRao Puli         res.jsonValue = {
192e5aaf047SAppaRao Puli             {"@odata.type",
193e5aaf047SAppaRao Puli              "#EventDestinationCollection.EventDestinationCollection"},
194e5aaf047SAppaRao Puli             {"@odata.id", "/redfish/v1/EventService/Subscriptions"},
195e5aaf047SAppaRao Puli             {"Name", "Event Destination Collections"}};
196e5aaf047SAppaRao Puli 
197e5aaf047SAppaRao Puli         nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
198e5aaf047SAppaRao Puli 
199b52664e2SAppaRao Puli         std::vector<std::string> subscripIds =
200b52664e2SAppaRao Puli             EventServiceManager::getInstance().getAllIDs();
201b52664e2SAppaRao Puli         memberArray = nlohmann::json::array();
202b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
203b52664e2SAppaRao Puli 
204b52664e2SAppaRao Puli         for (const std::string& id : subscripIds)
205e5aaf047SAppaRao Puli         {
206e5aaf047SAppaRao Puli             memberArray.push_back(
207e5aaf047SAppaRao Puli                 {{"@odata.id",
208b52664e2SAppaRao Puli                   "/redfish/v1/EventService/Subscriptions/" + id}});
209e5aaf047SAppaRao Puli         }
210e5aaf047SAppaRao Puli     }
211e5aaf047SAppaRao Puli 
212e5aaf047SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
213e5aaf047SAppaRao Puli                 const std::vector<std::string>& params) override
214e5aaf047SAppaRao Puli     {
215e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
216e5aaf047SAppaRao Puli 
217b52664e2SAppaRao Puli         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
218b52664e2SAppaRao Puli             maxNoOfSubscriptions)
219e5aaf047SAppaRao Puli         {
220e5aaf047SAppaRao Puli             messages::eventSubscriptionLimitExceeded(asyncResp->res);
221e5aaf047SAppaRao Puli             return;
222e5aaf047SAppaRao Puli         }
223e5aaf047SAppaRao Puli         std::string destUrl;
224e5aaf047SAppaRao Puli         std::string protocol;
225e5aaf047SAppaRao Puli         std::optional<std::string> context;
226e5aaf047SAppaRao Puli         std::optional<std::string> subscriptionType;
227e5aaf047SAppaRao Puli         std::optional<std::string> eventFormatType;
228e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
229e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> msgIds;
230e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> regPrefixes;
231e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
232156d6b00SAppaRao Puli         std::optional<std::vector<nlohmann::json>> metricReportDefinitions;
233e5aaf047SAppaRao Puli 
234e5aaf047SAppaRao Puli         if (!json_util::readJson(
235e5aaf047SAppaRao Puli                 req, res, "Destination", destUrl, "Context", context,
236e5aaf047SAppaRao Puli                 "Protocol", protocol, "SubscriptionType", subscriptionType,
237e5aaf047SAppaRao Puli                 "EventFormatType", eventFormatType, "HttpHeaders", headers,
238e5aaf047SAppaRao Puli                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
239156d6b00SAppaRao Puli                 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
240156d6b00SAppaRao Puli                 metricReportDefinitions))
241e5aaf047SAppaRao Puli         {
242e5aaf047SAppaRao Puli             return;
243e5aaf047SAppaRao Puli         }
244e5aaf047SAppaRao Puli 
245e5aaf047SAppaRao Puli         // Validate the URL using regex expression
246b52664e2SAppaRao Puli         // Format: <protocol>://<host>:<port>/<uri>
247b52664e2SAppaRao Puli         // protocol: http/https
248b52664e2SAppaRao Puli         // host: Exclude ' ', ':', '#', '?'
249b52664e2SAppaRao Puli         // port: Empty or numeric value with ':' seperator.
250b52664e2SAppaRao Puli         // uri: Start with '/' and Exclude '#', ' '
251b52664e2SAppaRao Puli         //      Can include query params(ex: '/event?test=1')
252b52664e2SAppaRao Puli         // TODO: Need to validate hostname extensively(as per rfc)
253b52664e2SAppaRao Puli         const std::regex urlRegex(
254b52664e2SAppaRao Puli             "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
255b52664e2SAppaRao Puli             "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
256b52664e2SAppaRao Puli         std::cmatch match;
257b52664e2SAppaRao Puli         if (!std::regex_match(destUrl.c_str(), match, urlRegex))
258e5aaf047SAppaRao Puli         {
259e5aaf047SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
260e5aaf047SAppaRao Puli                                                "Destination");
261e5aaf047SAppaRao Puli             return;
262e5aaf047SAppaRao Puli         }
263b52664e2SAppaRao Puli 
264b52664e2SAppaRao Puli         std::string uriProto = std::string(match[1].first, match[1].second);
265b52664e2SAppaRao Puli         if (uriProto == "http")
266b52664e2SAppaRao Puli         {
267b52664e2SAppaRao Puli #ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
268b52664e2SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
269b52664e2SAppaRao Puli                                                "Destination");
270b52664e2SAppaRao Puli             return;
271b52664e2SAppaRao Puli #endif
272b52664e2SAppaRao Puli         }
273b52664e2SAppaRao Puli 
274b52664e2SAppaRao Puli         std::string host = std::string(match[2].first, match[2].second);
275b52664e2SAppaRao Puli         std::string port = std::string(match[3].first, match[3].second);
276b52664e2SAppaRao Puli         std::string path = std::string(match[4].first, match[4].second);
277b52664e2SAppaRao Puli         if (port.empty())
278b52664e2SAppaRao Puli         {
279b52664e2SAppaRao Puli             if (uriProto == "http")
280b52664e2SAppaRao Puli             {
281b52664e2SAppaRao Puli                 port = "80";
282b52664e2SAppaRao Puli             }
283b52664e2SAppaRao Puli             else
284b52664e2SAppaRao Puli             {
285b52664e2SAppaRao Puli                 port = "443";
286b52664e2SAppaRao Puli             }
287b52664e2SAppaRao Puli         }
288b52664e2SAppaRao Puli         if (path.empty())
289b52664e2SAppaRao Puli         {
290b52664e2SAppaRao Puli             path = "/";
291b52664e2SAppaRao Puli         }
292b52664e2SAppaRao Puli 
293b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
294b52664e2SAppaRao Puli             std::make_shared<Subscription>(host, port, path, uriProto);
295b52664e2SAppaRao Puli 
296b52664e2SAppaRao Puli         subValue->destinationUrl = destUrl;
297e5aaf047SAppaRao Puli 
298e5aaf047SAppaRao Puli         if (subscriptionType)
299e5aaf047SAppaRao Puli         {
300e5aaf047SAppaRao Puli             if (*subscriptionType != "RedfishEvent")
301e5aaf047SAppaRao Puli             {
302e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
303e5aaf047SAppaRao Puli                     asyncResp->res, *subscriptionType, "SubscriptionType");
304e5aaf047SAppaRao Puli                 return;
305e5aaf047SAppaRao Puli             }
306b52664e2SAppaRao Puli             subValue->subscriptionType = *subscriptionType;
307e5aaf047SAppaRao Puli         }
308e5aaf047SAppaRao Puli         else
309e5aaf047SAppaRao Puli         {
310b52664e2SAppaRao Puli             subValue->subscriptionType = "RedfishEvent"; // Default
311e5aaf047SAppaRao Puli         }
312e5aaf047SAppaRao Puli 
313e5aaf047SAppaRao Puli         if (protocol != "Redfish")
314e5aaf047SAppaRao Puli         {
315e5aaf047SAppaRao Puli             messages::propertyValueNotInList(asyncResp->res, protocol,
316e5aaf047SAppaRao Puli                                              "Protocol");
317e5aaf047SAppaRao Puli             return;
318e5aaf047SAppaRao Puli         }
319b52664e2SAppaRao Puli         subValue->protocol = protocol;
320e5aaf047SAppaRao Puli 
321e5aaf047SAppaRao Puli         if (eventFormatType)
322e5aaf047SAppaRao Puli         {
323e5aaf047SAppaRao Puli             if (std::find(supportedEvtFormatTypes.begin(),
324e5aaf047SAppaRao Puli                           supportedEvtFormatTypes.end(),
325e5aaf047SAppaRao Puli                           *eventFormatType) == supportedEvtFormatTypes.end())
326e5aaf047SAppaRao Puli             {
327e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
328e5aaf047SAppaRao Puli                     asyncResp->res, *eventFormatType, "EventFormatType");
329e5aaf047SAppaRao Puli                 return;
330e5aaf047SAppaRao Puli             }
331b52664e2SAppaRao Puli             subValue->eventFormatType = *eventFormatType;
332e5aaf047SAppaRao Puli         }
333e5aaf047SAppaRao Puli         else
334e5aaf047SAppaRao Puli         {
335e5aaf047SAppaRao Puli             // If not specified, use default "Event"
336b52664e2SAppaRao Puli             subValue->eventFormatType.assign({"Event"});
337e5aaf047SAppaRao Puli         }
338e5aaf047SAppaRao Puli 
339e5aaf047SAppaRao Puli         if (context)
340e5aaf047SAppaRao Puli         {
341b52664e2SAppaRao Puli             subValue->customText = *context;
342e5aaf047SAppaRao Puli         }
343e5aaf047SAppaRao Puli 
344e5aaf047SAppaRao Puli         if (headers)
345e5aaf047SAppaRao Puli         {
346b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
347e5aaf047SAppaRao Puli         }
348e5aaf047SAppaRao Puli 
349e5aaf047SAppaRao Puli         if (regPrefixes)
350e5aaf047SAppaRao Puli         {
351e5aaf047SAppaRao Puli             for (const std::string& it : *regPrefixes)
352e5aaf047SAppaRao Puli             {
353e5aaf047SAppaRao Puli                 if (std::find(supportedRegPrefixes.begin(),
354e5aaf047SAppaRao Puli                               supportedRegPrefixes.end(),
355e5aaf047SAppaRao Puli                               it) == supportedRegPrefixes.end())
356e5aaf047SAppaRao Puli                 {
357e5aaf047SAppaRao Puli                     messages::propertyValueNotInList(asyncResp->res, it,
358e5aaf047SAppaRao Puli                                                      "RegistryPrefixes");
359e5aaf047SAppaRao Puli                     return;
360e5aaf047SAppaRao Puli                 }
361e5aaf047SAppaRao Puli             }
362b52664e2SAppaRao Puli             subValue->registryPrefixes = *regPrefixes;
363e5aaf047SAppaRao Puli         }
364e5aaf047SAppaRao Puli 
365e5aaf047SAppaRao Puli         if (msgIds)
366e5aaf047SAppaRao Puli         {
367e5aaf047SAppaRao Puli             // Do we need to loop-up MessageRegistry and validate
368e5aaf047SAppaRao Puli             // data for authenticity??? Not mandate, i believe.
369b52664e2SAppaRao Puli             subValue->registryMsgIds = *msgIds;
370e5aaf047SAppaRao Puli         }
371e5aaf047SAppaRao Puli 
372e5aaf047SAppaRao Puli         if (retryPolicy)
373e5aaf047SAppaRao Puli         {
374e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
375e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
376e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
377e5aaf047SAppaRao Puli             {
378e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
379e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
380e5aaf047SAppaRao Puli                 return;
381e5aaf047SAppaRao Puli             }
382b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
383e5aaf047SAppaRao Puli         }
384e5aaf047SAppaRao Puli         else
385e5aaf047SAppaRao Puli         {
386e5aaf047SAppaRao Puli             // Default "TerminateAfterRetries"
387b52664e2SAppaRao Puli             subValue->retryPolicy = "TerminateAfterRetries";
388e5aaf047SAppaRao Puli         }
389e5aaf047SAppaRao Puli 
390156d6b00SAppaRao Puli         if (metricReportDefinitions)
391156d6b00SAppaRao Puli         {
392156d6b00SAppaRao Puli             subValue->metricReportDefinitions = *metricReportDefinitions;
393156d6b00SAppaRao Puli         }
394156d6b00SAppaRao Puli 
395b52664e2SAppaRao Puli         std::string id =
396b52664e2SAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue);
397b52664e2SAppaRao Puli         if (id.empty())
398e5aaf047SAppaRao Puli         {
399e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
400e5aaf047SAppaRao Puli             return;
401e5aaf047SAppaRao Puli         }
402e5aaf047SAppaRao Puli 
403e5aaf047SAppaRao Puli         messages::created(asyncResp->res);
404e5aaf047SAppaRao Puli         asyncResp->res.addHeader(
405e5aaf047SAppaRao Puli             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
406e5aaf047SAppaRao Puli     }
407e5aaf047SAppaRao Puli };
408e5aaf047SAppaRao Puli 
4094bbf237fSAppaRao Puli class EventServiceSSE : public Node
4104bbf237fSAppaRao Puli {
4114bbf237fSAppaRao Puli   public:
4124bbf237fSAppaRao Puli     EventServiceSSE(CrowApp& app) :
4134bbf237fSAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/SSE/")
4144bbf237fSAppaRao Puli     {
4154bbf237fSAppaRao Puli         entityPrivileges = {
4164bbf237fSAppaRao Puli             {boost::beast::http::verb::get, {{"ConfigureManager"}}},
4174bbf237fSAppaRao Puli             {boost::beast::http::verb::head, {{"ConfigureManager"}}},
4184bbf237fSAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
4194bbf237fSAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
4204bbf237fSAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
4214bbf237fSAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
4224bbf237fSAppaRao Puli     }
4234bbf237fSAppaRao Puli 
4244bbf237fSAppaRao Puli   private:
4254bbf237fSAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
4264bbf237fSAppaRao Puli                const std::vector<std::string>& params) override
4274bbf237fSAppaRao Puli     {
4284bbf237fSAppaRao Puli         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
4294bbf237fSAppaRao Puli             maxNoOfSubscriptions)
4304bbf237fSAppaRao Puli         {
4314bbf237fSAppaRao Puli             messages::eventSubscriptionLimitExceeded(res);
4324bbf237fSAppaRao Puli             res.end();
4334bbf237fSAppaRao Puli             return;
4344bbf237fSAppaRao Puli         }
4354bbf237fSAppaRao Puli 
4364bbf237fSAppaRao Puli         std::shared_ptr<crow::Request::Adaptor> sseConn =
4374bbf237fSAppaRao Puli             std::make_shared<crow::Request::Adaptor>(std::move(req.socket()));
4384bbf237fSAppaRao Puli         std::shared_ptr<Subscription> subValue =
4394bbf237fSAppaRao Puli             std::make_shared<Subscription>(sseConn);
4404bbf237fSAppaRao Puli 
4414bbf237fSAppaRao Puli         // GET on this URI means, Its SSE subscriptionType.
4424bbf237fSAppaRao Puli         subValue->subscriptionType = "SSE";
443*07941a88SAyushi Smriti 
444*07941a88SAyushi Smriti         // Default values
4454bbf237fSAppaRao Puli         subValue->protocol = "Redfish";
446*07941a88SAyushi Smriti         subValue->retryPolicy = "TerminateAfterRetries";
4474bbf237fSAppaRao Puli 
4484bbf237fSAppaRao Puli         char* filters = req.urlParams.get("$filter");
4494bbf237fSAppaRao Puli         if (filters == nullptr)
4504bbf237fSAppaRao Puli         {
4514bbf237fSAppaRao Puli             subValue->eventFormatType = "Event";
4524bbf237fSAppaRao Puli         }
4534bbf237fSAppaRao Puli         else
4544bbf237fSAppaRao Puli         {
455*07941a88SAyushi Smriti             // Reading from query params.
456*07941a88SAyushi Smriti             bool status = readSSEQueryParams(
457*07941a88SAyushi Smriti                 filters, subValue->eventFormatType, subValue->registryMsgIds,
458*07941a88SAyushi Smriti                 subValue->registryPrefixes, subValue->metricReportDefinitions);
459*07941a88SAyushi Smriti 
460*07941a88SAyushi Smriti             if (!status)
461*07941a88SAyushi Smriti             {
462*07941a88SAyushi Smriti                 messages::invalidObject(res, filters);
463*07941a88SAyushi Smriti                 return;
464*07941a88SAyushi Smriti             }
465*07941a88SAyushi Smriti 
466*07941a88SAyushi Smriti             if (!subValue->eventFormatType.empty())
467*07941a88SAyushi Smriti             {
468*07941a88SAyushi Smriti                 if (std::find(supportedEvtFormatTypes.begin(),
469*07941a88SAyushi Smriti                               supportedEvtFormatTypes.end(),
470*07941a88SAyushi Smriti                               subValue->eventFormatType) ==
471*07941a88SAyushi Smriti                     supportedEvtFormatTypes.end())
472*07941a88SAyushi Smriti                 {
473*07941a88SAyushi Smriti                     messages::propertyValueNotInList(
474*07941a88SAyushi Smriti                         res, subValue->eventFormatType, "EventFormatType");
475*07941a88SAyushi Smriti                     return;
476*07941a88SAyushi Smriti                 }
477*07941a88SAyushi Smriti             }
478*07941a88SAyushi Smriti             else
479*07941a88SAyushi Smriti             {
480*07941a88SAyushi Smriti                 // If nothing specified, using default "Event"
481*07941a88SAyushi Smriti                 subValue->eventFormatType.assign({"Event"});
482*07941a88SAyushi Smriti             }
483*07941a88SAyushi Smriti 
484*07941a88SAyushi Smriti             if (!subValue->registryPrefixes.empty())
485*07941a88SAyushi Smriti             {
486*07941a88SAyushi Smriti                 for (const std::string& it : subValue->registryPrefixes)
487*07941a88SAyushi Smriti                 {
488*07941a88SAyushi Smriti                     if (std::find(supportedRegPrefixes.begin(),
489*07941a88SAyushi Smriti                                   supportedRegPrefixes.end(),
490*07941a88SAyushi Smriti                                   it) == supportedRegPrefixes.end())
491*07941a88SAyushi Smriti                     {
492*07941a88SAyushi Smriti                         messages::propertyValueNotInList(res, it,
493*07941a88SAyushi Smriti                                                          "RegistryPrefixes");
494*07941a88SAyushi Smriti                         return;
495*07941a88SAyushi Smriti                     }
496*07941a88SAyushi Smriti                 }
497*07941a88SAyushi Smriti             }
4984bbf237fSAppaRao Puli         }
4994bbf237fSAppaRao Puli 
5004bbf237fSAppaRao Puli         std::string id =
5014bbf237fSAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue, false);
5024bbf237fSAppaRao Puli         if (id.empty())
5034bbf237fSAppaRao Puli         {
5044bbf237fSAppaRao Puli             messages::internalError(res);
5054bbf237fSAppaRao Puli             res.end();
5064bbf237fSAppaRao Puli             return;
5074bbf237fSAppaRao Puli         }
5084bbf237fSAppaRao Puli     }
5094bbf237fSAppaRao Puli };
5104bbf237fSAppaRao Puli 
511e5aaf047SAppaRao Puli class EventDestination : public Node
512e5aaf047SAppaRao Puli {
513e5aaf047SAppaRao Puli   public:
514e5aaf047SAppaRao Puli     EventDestination(CrowApp& app) :
515e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
516e5aaf047SAppaRao Puli              std::string())
517e5aaf047SAppaRao Puli     {
518e5aaf047SAppaRao Puli         entityPrivileges = {
519e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
520e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
521e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
522e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
523e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
524e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
525e5aaf047SAppaRao Puli     }
526e5aaf047SAppaRao Puli 
527e5aaf047SAppaRao Puli   private:
528e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
529e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
530e5aaf047SAppaRao Puli     {
531e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
532e5aaf047SAppaRao Puli         if (params.size() != 1)
533e5aaf047SAppaRao Puli         {
534e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
535e5aaf047SAppaRao Puli             return;
536e5aaf047SAppaRao Puli         }
537e5aaf047SAppaRao Puli 
538b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
539b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
540b52664e2SAppaRao Puli         if (subValue == nullptr)
541e5aaf047SAppaRao Puli         {
542e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
543e5aaf047SAppaRao Puli             res.end();
544e5aaf047SAppaRao Puli             return;
545e5aaf047SAppaRao Puli         }
546b52664e2SAppaRao Puli         const std::string& id = params[0];
547e5aaf047SAppaRao Puli 
548e5aaf047SAppaRao Puli         res.jsonValue = {
549e5aaf047SAppaRao Puli             {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
550e5aaf047SAppaRao Puli             {"Protocol", "Redfish"}};
551e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["@odata.id"] =
552e5aaf047SAppaRao Puli             "/redfish/v1/EventService/Subscriptions/" + id;
553e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Id"] = id;
554e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
555b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
556b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Context"] = subValue->customText;
557e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["SubscriptionType"] =
558b52664e2SAppaRao Puli             subValue->subscriptionType;
559b52664e2SAppaRao Puli         asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
560b52664e2SAppaRao Puli         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
561e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] =
562b52664e2SAppaRao Puli             subValue->registryPrefixes;
563b52664e2SAppaRao Puli         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
564b52664e2SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
565156d6b00SAppaRao Puli         asyncResp->res.jsonValue["MetricReportDefinitions"] =
566156d6b00SAppaRao Puli             subValue->metricReportDefinitions;
567e5aaf047SAppaRao Puli     }
568e5aaf047SAppaRao Puli 
569e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
570e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
571e5aaf047SAppaRao Puli     {
572e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
573e5aaf047SAppaRao Puli         if (params.size() != 1)
574e5aaf047SAppaRao Puli         {
575e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
576e5aaf047SAppaRao Puli             return;
577e5aaf047SAppaRao Puli         }
578e5aaf047SAppaRao Puli 
579b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
580b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
581b52664e2SAppaRao Puli         if (subValue == nullptr)
582e5aaf047SAppaRao Puli         {
583e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
584e5aaf047SAppaRao Puli             res.end();
585e5aaf047SAppaRao Puli             return;
586e5aaf047SAppaRao Puli         }
587e5aaf047SAppaRao Puli 
588e5aaf047SAppaRao Puli         std::optional<std::string> context;
589e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
590e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
591e5aaf047SAppaRao Puli 
592e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "Context", context,
593e5aaf047SAppaRao Puli                                  "DeliveryRetryPolicy", retryPolicy,
594e5aaf047SAppaRao Puli                                  "HttpHeaders", headers))
595e5aaf047SAppaRao Puli         {
596e5aaf047SAppaRao Puli             return;
597e5aaf047SAppaRao Puli         }
598e5aaf047SAppaRao Puli 
599e5aaf047SAppaRao Puli         if (context)
600e5aaf047SAppaRao Puli         {
601b52664e2SAppaRao Puli             subValue->customText = *context;
602e5aaf047SAppaRao Puli         }
603e5aaf047SAppaRao Puli 
604e5aaf047SAppaRao Puli         if (headers)
605e5aaf047SAppaRao Puli         {
606b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
607e5aaf047SAppaRao Puli         }
608e5aaf047SAppaRao Puli 
609e5aaf047SAppaRao Puli         if (retryPolicy)
610e5aaf047SAppaRao Puli         {
611e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
612e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
613e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
614e5aaf047SAppaRao Puli             {
615e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
616e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
617e5aaf047SAppaRao Puli                 return;
618e5aaf047SAppaRao Puli             }
619b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
620fe44eb0bSAyushi Smriti             subValue->updateRetryPolicy();
621e5aaf047SAppaRao Puli         }
622e5aaf047SAppaRao Puli 
623b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
624e5aaf047SAppaRao Puli     }
625e5aaf047SAppaRao Puli 
626e5aaf047SAppaRao Puli     void doDelete(crow::Response& res, const crow::Request& req,
627e5aaf047SAppaRao Puli                   const std::vector<std::string>& params) override
628e5aaf047SAppaRao Puli     {
629e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
630e5aaf047SAppaRao Puli 
631e5aaf047SAppaRao Puli         if (params.size() != 1)
632e5aaf047SAppaRao Puli         {
633e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
634e5aaf047SAppaRao Puli             return;
635e5aaf047SAppaRao Puli         }
636e5aaf047SAppaRao Puli 
637b52664e2SAppaRao Puli         if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
638e5aaf047SAppaRao Puli         {
639e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
640e5aaf047SAppaRao Puli             res.end();
641e5aaf047SAppaRao Puli             return;
642e5aaf047SAppaRao Puli         }
643b52664e2SAppaRao Puli         EventServiceManager::getInstance().deleteSubscription(params[0]);
644e5aaf047SAppaRao Puli     }
645e5aaf047SAppaRao Puli };
646e5aaf047SAppaRao Puli 
647e5aaf047SAppaRao Puli } // namespace redfish
648