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 
22*156d6b00SAppaRao Puli static constexpr const std::array<const char*, 2> supportedEvtFormatTypes = {
23*156d6b00SAppaRao 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 
64e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
65b52664e2SAppaRao Puli         asyncResp->res.jsonValue["ServiceEnabled"] =
66b52664e2SAppaRao Puli             EventServiceManager::getInstance().enabled;
67e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
68b52664e2SAppaRao Puli             EventServiceManager::getInstance().retryAttempts;
69e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
70b52664e2SAppaRao Puli             EventServiceManager::getInstance().retryTimeoutInterval;
71e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["EventFormatTypes"] = supportedEvtFormatTypes;
72e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] = supportedRegPrefixes;
73e5aaf047SAppaRao Puli     }
74e5aaf047SAppaRao Puli 
75e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
76e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
77e5aaf047SAppaRao Puli     {
78e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
79e5aaf047SAppaRao Puli 
80e5aaf047SAppaRao Puli         std::optional<bool> serviceEnabled;
81e5aaf047SAppaRao Puli         std::optional<uint32_t> retryAttemps;
82e5aaf047SAppaRao Puli         std::optional<uint32_t> retryInterval;
83e5aaf047SAppaRao Puli 
84e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "ServiceEnabled", serviceEnabled,
85e5aaf047SAppaRao Puli                                  "DeliveryRetryAttempts", retryAttemps,
86e5aaf047SAppaRao Puli                                  "DeliveryRetryIntervalSeconds", retryInterval))
87e5aaf047SAppaRao Puli         {
88e5aaf047SAppaRao Puli             return;
89e5aaf047SAppaRao Puli         }
90e5aaf047SAppaRao Puli 
91e5aaf047SAppaRao Puli         if (serviceEnabled)
92e5aaf047SAppaRao Puli         {
93b52664e2SAppaRao Puli             EventServiceManager::getInstance().enabled = *serviceEnabled;
94e5aaf047SAppaRao Puli         }
95e5aaf047SAppaRao Puli 
96e5aaf047SAppaRao Puli         if (retryAttemps)
97e5aaf047SAppaRao Puli         {
98e5aaf047SAppaRao Puli             // Supported range [1-3]
99e5aaf047SAppaRao Puli             if ((*retryAttemps < 1) || (*retryAttemps > 3))
100e5aaf047SAppaRao Puli             {
101e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
102e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryAttemps),
103e5aaf047SAppaRao Puli                     "DeliveryRetryAttempts", "[1-3]");
104e5aaf047SAppaRao Puli             }
105e5aaf047SAppaRao Puli             else
106e5aaf047SAppaRao Puli             {
107b52664e2SAppaRao Puli                 EventServiceManager::getInstance().retryAttempts =
108b52664e2SAppaRao Puli                     *retryAttemps;
109e5aaf047SAppaRao Puli             }
110e5aaf047SAppaRao Puli         }
111e5aaf047SAppaRao Puli 
112e5aaf047SAppaRao Puli         if (retryInterval)
113e5aaf047SAppaRao Puli         {
114e5aaf047SAppaRao Puli             // Supported range [30 - 180]
115e5aaf047SAppaRao Puli             if ((*retryInterval < 30) || (*retryInterval > 180))
116e5aaf047SAppaRao Puli             {
117e5aaf047SAppaRao Puli                 messages::queryParameterOutOfRange(
118e5aaf047SAppaRao Puli                     asyncResp->res, std::to_string(*retryInterval),
119e5aaf047SAppaRao Puli                     "DeliveryRetryIntervalSeconds", "[30-180]");
120e5aaf047SAppaRao Puli             }
121e5aaf047SAppaRao Puli             else
122e5aaf047SAppaRao Puli             {
123b52664e2SAppaRao Puli                 EventServiceManager::getInstance().retryTimeoutInterval =
124b52664e2SAppaRao Puli                     *retryInterval;
125e5aaf047SAppaRao Puli             }
126e5aaf047SAppaRao Puli         }
127e5aaf047SAppaRao Puli 
128b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
129e5aaf047SAppaRao Puli     }
130e5aaf047SAppaRao Puli };
131e5aaf047SAppaRao Puli 
1320b4bdd93SAppaRao Puli class SubmitTestEvent : public Node
1330b4bdd93SAppaRao Puli {
1340b4bdd93SAppaRao Puli   public:
1350b4bdd93SAppaRao Puli     SubmitTestEvent(CrowApp& app) :
1360b4bdd93SAppaRao Puli         Node(app,
1370b4bdd93SAppaRao Puli              "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
1380b4bdd93SAppaRao Puli     {
1390b4bdd93SAppaRao Puli         entityPrivileges = {
1400b4bdd93SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
1410b4bdd93SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
1420b4bdd93SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1430b4bdd93SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1440b4bdd93SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1450b4bdd93SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1460b4bdd93SAppaRao Puli     }
1470b4bdd93SAppaRao Puli 
1480b4bdd93SAppaRao Puli   private:
1490b4bdd93SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
1500b4bdd93SAppaRao Puli                 const std::vector<std::string>& params) override
1510b4bdd93SAppaRao Puli     {
1520b4bdd93SAppaRao Puli         EventServiceManager::getInstance().sendTestEventLog();
1530b4bdd93SAppaRao Puli         res.result(boost::beast::http::status::no_content);
1540b4bdd93SAppaRao Puli         res.end();
1550b4bdd93SAppaRao Puli     }
1560b4bdd93SAppaRao Puli };
1570b4bdd93SAppaRao Puli 
158e5aaf047SAppaRao Puli class EventDestinationCollection : public Node
159e5aaf047SAppaRao Puli {
160e5aaf047SAppaRao Puli   public:
161e5aaf047SAppaRao Puli     EventDestinationCollection(CrowApp& app) :
162e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/")
163e5aaf047SAppaRao Puli     {
164e5aaf047SAppaRao Puli         entityPrivileges = {
165e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
166e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
167e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
168e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
169e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
170e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
171e5aaf047SAppaRao Puli     }
172e5aaf047SAppaRao Puli 
173e5aaf047SAppaRao Puli   private:
174e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
175e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
176e5aaf047SAppaRao Puli     {
177e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
178e5aaf047SAppaRao Puli 
179e5aaf047SAppaRao Puli         res.jsonValue = {
180e5aaf047SAppaRao Puli             {"@odata.type",
181e5aaf047SAppaRao Puli              "#EventDestinationCollection.EventDestinationCollection"},
182e5aaf047SAppaRao Puli             {"@odata.id", "/redfish/v1/EventService/Subscriptions"},
183e5aaf047SAppaRao Puli             {"Name", "Event Destination Collections"}};
184e5aaf047SAppaRao Puli 
185e5aaf047SAppaRao Puli         nlohmann::json& memberArray = asyncResp->res.jsonValue["Members"];
186e5aaf047SAppaRao Puli 
187b52664e2SAppaRao Puli         std::vector<std::string> subscripIds =
188b52664e2SAppaRao Puli             EventServiceManager::getInstance().getAllIDs();
189b52664e2SAppaRao Puli         memberArray = nlohmann::json::array();
190b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Members@odata.count"] = subscripIds.size();
191b52664e2SAppaRao Puli 
192b52664e2SAppaRao Puli         for (const std::string& id : subscripIds)
193e5aaf047SAppaRao Puli         {
194e5aaf047SAppaRao Puli             memberArray.push_back(
195e5aaf047SAppaRao Puli                 {{"@odata.id",
196b52664e2SAppaRao Puli                   "/redfish/v1/EventService/Subscriptions/" + id}});
197e5aaf047SAppaRao Puli         }
198e5aaf047SAppaRao Puli     }
199e5aaf047SAppaRao Puli 
200e5aaf047SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
201e5aaf047SAppaRao Puli                 const std::vector<std::string>& params) override
202e5aaf047SAppaRao Puli     {
203e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
204e5aaf047SAppaRao Puli 
205b52664e2SAppaRao Puli         if (EventServiceManager::getInstance().getNumberOfSubscriptions() >=
206b52664e2SAppaRao Puli             maxNoOfSubscriptions)
207e5aaf047SAppaRao Puli         {
208e5aaf047SAppaRao Puli             messages::eventSubscriptionLimitExceeded(asyncResp->res);
209e5aaf047SAppaRao Puli             return;
210e5aaf047SAppaRao Puli         }
211e5aaf047SAppaRao Puli         std::string destUrl;
212e5aaf047SAppaRao Puli         std::string protocol;
213e5aaf047SAppaRao Puli         std::optional<std::string> context;
214e5aaf047SAppaRao Puli         std::optional<std::string> subscriptionType;
215e5aaf047SAppaRao Puli         std::optional<std::string> eventFormatType;
216e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
217e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> msgIds;
218e5aaf047SAppaRao Puli         std::optional<std::vector<std::string>> regPrefixes;
219e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
220*156d6b00SAppaRao Puli         std::optional<std::vector<nlohmann::json>> metricReportDefinitions;
221e5aaf047SAppaRao Puli 
222e5aaf047SAppaRao Puli         if (!json_util::readJson(
223e5aaf047SAppaRao Puli                 req, res, "Destination", destUrl, "Context", context,
224e5aaf047SAppaRao Puli                 "Protocol", protocol, "SubscriptionType", subscriptionType,
225e5aaf047SAppaRao Puli                 "EventFormatType", eventFormatType, "HttpHeaders", headers,
226e5aaf047SAppaRao Puli                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
227*156d6b00SAppaRao Puli                 "DeliveryRetryPolicy", retryPolicy, "MetricReportDefinitions",
228*156d6b00SAppaRao Puli                 metricReportDefinitions))
229e5aaf047SAppaRao Puli         {
230e5aaf047SAppaRao Puli             return;
231e5aaf047SAppaRao Puli         }
232e5aaf047SAppaRao Puli 
233e5aaf047SAppaRao Puli         // Validate the URL using regex expression
234b52664e2SAppaRao Puli         // Format: <protocol>://<host>:<port>/<uri>
235b52664e2SAppaRao Puli         // protocol: http/https
236b52664e2SAppaRao Puli         // host: Exclude ' ', ':', '#', '?'
237b52664e2SAppaRao Puli         // port: Empty or numeric value with ':' seperator.
238b52664e2SAppaRao Puli         // uri: Start with '/' and Exclude '#', ' '
239b52664e2SAppaRao Puli         //      Can include query params(ex: '/event?test=1')
240b52664e2SAppaRao Puli         // TODO: Need to validate hostname extensively(as per rfc)
241b52664e2SAppaRao Puli         const std::regex urlRegex(
242b52664e2SAppaRao Puli             "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
243b52664e2SAppaRao Puli             "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
244b52664e2SAppaRao Puli         std::cmatch match;
245b52664e2SAppaRao Puli         if (!std::regex_match(destUrl.c_str(), match, urlRegex))
246e5aaf047SAppaRao Puli         {
247e5aaf047SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
248e5aaf047SAppaRao Puli                                                "Destination");
249e5aaf047SAppaRao Puli             return;
250e5aaf047SAppaRao Puli         }
251b52664e2SAppaRao Puli 
252b52664e2SAppaRao Puli         std::string uriProto = std::string(match[1].first, match[1].second);
253b52664e2SAppaRao Puli         if (uriProto == "http")
254b52664e2SAppaRao Puli         {
255b52664e2SAppaRao Puli #ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
256b52664e2SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
257b52664e2SAppaRao Puli                                                "Destination");
258b52664e2SAppaRao Puli             return;
259b52664e2SAppaRao Puli #endif
260b52664e2SAppaRao Puli         }
261b52664e2SAppaRao Puli 
262b52664e2SAppaRao Puli         std::string host = std::string(match[2].first, match[2].second);
263b52664e2SAppaRao Puli         std::string port = std::string(match[3].first, match[3].second);
264b52664e2SAppaRao Puli         std::string path = std::string(match[4].first, match[4].second);
265b52664e2SAppaRao Puli         if (port.empty())
266b52664e2SAppaRao Puli         {
267b52664e2SAppaRao Puli             if (uriProto == "http")
268b52664e2SAppaRao Puli             {
269b52664e2SAppaRao Puli                 port = "80";
270b52664e2SAppaRao Puli             }
271b52664e2SAppaRao Puli             else
272b52664e2SAppaRao Puli             {
273b52664e2SAppaRao Puli                 port = "443";
274b52664e2SAppaRao Puli             }
275b52664e2SAppaRao Puli         }
276b52664e2SAppaRao Puli         if (path.empty())
277b52664e2SAppaRao Puli         {
278b52664e2SAppaRao Puli             path = "/";
279b52664e2SAppaRao Puli         }
280b52664e2SAppaRao Puli 
281b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
282b52664e2SAppaRao Puli             std::make_shared<Subscription>(host, port, path, uriProto);
283b52664e2SAppaRao Puli 
284b52664e2SAppaRao Puli         subValue->destinationUrl = destUrl;
285e5aaf047SAppaRao Puli 
286e5aaf047SAppaRao Puli         if (subscriptionType)
287e5aaf047SAppaRao Puli         {
288e5aaf047SAppaRao Puli             if (*subscriptionType != "RedfishEvent")
289e5aaf047SAppaRao Puli             {
290e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
291e5aaf047SAppaRao Puli                     asyncResp->res, *subscriptionType, "SubscriptionType");
292e5aaf047SAppaRao Puli                 return;
293e5aaf047SAppaRao Puli             }
294b52664e2SAppaRao Puli             subValue->subscriptionType = *subscriptionType;
295e5aaf047SAppaRao Puli         }
296e5aaf047SAppaRao Puli         else
297e5aaf047SAppaRao Puli         {
298b52664e2SAppaRao Puli             subValue->subscriptionType = "RedfishEvent"; // Default
299e5aaf047SAppaRao Puli         }
300e5aaf047SAppaRao Puli 
301e5aaf047SAppaRao Puli         if (protocol != "Redfish")
302e5aaf047SAppaRao Puli         {
303e5aaf047SAppaRao Puli             messages::propertyValueNotInList(asyncResp->res, protocol,
304e5aaf047SAppaRao Puli                                              "Protocol");
305e5aaf047SAppaRao Puli             return;
306e5aaf047SAppaRao Puli         }
307b52664e2SAppaRao Puli         subValue->protocol = protocol;
308e5aaf047SAppaRao Puli 
309e5aaf047SAppaRao Puli         if (eventFormatType)
310e5aaf047SAppaRao Puli         {
311e5aaf047SAppaRao Puli             if (std::find(supportedEvtFormatTypes.begin(),
312e5aaf047SAppaRao Puli                           supportedEvtFormatTypes.end(),
313e5aaf047SAppaRao Puli                           *eventFormatType) == supportedEvtFormatTypes.end())
314e5aaf047SAppaRao Puli             {
315e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
316e5aaf047SAppaRao Puli                     asyncResp->res, *eventFormatType, "EventFormatType");
317e5aaf047SAppaRao Puli                 return;
318e5aaf047SAppaRao Puli             }
319b52664e2SAppaRao Puli             subValue->eventFormatType = *eventFormatType;
320e5aaf047SAppaRao Puli         }
321e5aaf047SAppaRao Puli         else
322e5aaf047SAppaRao Puli         {
323e5aaf047SAppaRao Puli             // If not specified, use default "Event"
324b52664e2SAppaRao Puli             subValue->eventFormatType.assign({"Event"});
325e5aaf047SAppaRao Puli         }
326e5aaf047SAppaRao Puli 
327e5aaf047SAppaRao Puli         if (context)
328e5aaf047SAppaRao Puli         {
329b52664e2SAppaRao Puli             subValue->customText = *context;
330e5aaf047SAppaRao Puli         }
331e5aaf047SAppaRao Puli 
332e5aaf047SAppaRao Puli         if (headers)
333e5aaf047SAppaRao Puli         {
334b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
335e5aaf047SAppaRao Puli         }
336e5aaf047SAppaRao Puli 
337e5aaf047SAppaRao Puli         if (regPrefixes)
338e5aaf047SAppaRao Puli         {
339e5aaf047SAppaRao Puli             for (const std::string& it : *regPrefixes)
340e5aaf047SAppaRao Puli             {
341e5aaf047SAppaRao Puli                 if (std::find(supportedRegPrefixes.begin(),
342e5aaf047SAppaRao Puli                               supportedRegPrefixes.end(),
343e5aaf047SAppaRao Puli                               it) == supportedRegPrefixes.end())
344e5aaf047SAppaRao Puli                 {
345e5aaf047SAppaRao Puli                     messages::propertyValueNotInList(asyncResp->res, it,
346e5aaf047SAppaRao Puli                                                      "RegistryPrefixes");
347e5aaf047SAppaRao Puli                     return;
348e5aaf047SAppaRao Puli                 }
349e5aaf047SAppaRao Puli             }
350b52664e2SAppaRao Puli             subValue->registryPrefixes = *regPrefixes;
351e5aaf047SAppaRao Puli         }
352e5aaf047SAppaRao Puli 
353e5aaf047SAppaRao Puli         if (msgIds)
354e5aaf047SAppaRao Puli         {
355e5aaf047SAppaRao Puli             // Do we need to loop-up MessageRegistry and validate
356e5aaf047SAppaRao Puli             // data for authenticity??? Not mandate, i believe.
357b52664e2SAppaRao Puli             subValue->registryMsgIds = *msgIds;
358e5aaf047SAppaRao Puli         }
359e5aaf047SAppaRao Puli 
360e5aaf047SAppaRao Puli         if (retryPolicy)
361e5aaf047SAppaRao Puli         {
362e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
363e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
364e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
365e5aaf047SAppaRao Puli             {
366e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
367e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
368e5aaf047SAppaRao Puli                 return;
369e5aaf047SAppaRao Puli             }
370b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
371e5aaf047SAppaRao Puli         }
372e5aaf047SAppaRao Puli         else
373e5aaf047SAppaRao Puli         {
374e5aaf047SAppaRao Puli             // Default "TerminateAfterRetries"
375b52664e2SAppaRao Puli             subValue->retryPolicy = "TerminateAfterRetries";
376e5aaf047SAppaRao Puli         }
377e5aaf047SAppaRao Puli 
378*156d6b00SAppaRao Puli         if (metricReportDefinitions)
379*156d6b00SAppaRao Puli         {
380*156d6b00SAppaRao Puli             subValue->metricReportDefinitions = *metricReportDefinitions;
381*156d6b00SAppaRao Puli         }
382*156d6b00SAppaRao Puli 
383b52664e2SAppaRao Puli         std::string id =
384b52664e2SAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue);
385b52664e2SAppaRao Puli         if (id.empty())
386e5aaf047SAppaRao Puli         {
387e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
388e5aaf047SAppaRao Puli             return;
389e5aaf047SAppaRao Puli         }
390e5aaf047SAppaRao Puli 
391e5aaf047SAppaRao Puli         messages::created(asyncResp->res);
392e5aaf047SAppaRao Puli         asyncResp->res.addHeader(
393e5aaf047SAppaRao Puli             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
394e5aaf047SAppaRao Puli     }
395e5aaf047SAppaRao Puli };
396e5aaf047SAppaRao Puli 
397e5aaf047SAppaRao Puli class EventDestination : public Node
398e5aaf047SAppaRao Puli {
399e5aaf047SAppaRao Puli   public:
400e5aaf047SAppaRao Puli     EventDestination(CrowApp& app) :
401e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
402e5aaf047SAppaRao Puli              std::string())
403e5aaf047SAppaRao Puli     {
404e5aaf047SAppaRao Puli         entityPrivileges = {
405e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
406e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
407e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
408e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
409e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
410e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
411e5aaf047SAppaRao Puli     }
412e5aaf047SAppaRao Puli 
413e5aaf047SAppaRao Puli   private:
414e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
415e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
416e5aaf047SAppaRao Puli     {
417e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
418e5aaf047SAppaRao Puli         if (params.size() != 1)
419e5aaf047SAppaRao Puli         {
420e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
421e5aaf047SAppaRao Puli             return;
422e5aaf047SAppaRao Puli         }
423e5aaf047SAppaRao Puli 
424b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
425b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
426b52664e2SAppaRao Puli         if (subValue == nullptr)
427e5aaf047SAppaRao Puli         {
428e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
429e5aaf047SAppaRao Puli             res.end();
430e5aaf047SAppaRao Puli             return;
431e5aaf047SAppaRao Puli         }
432b52664e2SAppaRao Puli         const std::string& id = params[0];
433e5aaf047SAppaRao Puli 
434e5aaf047SAppaRao Puli         res.jsonValue = {
435e5aaf047SAppaRao Puli             {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
436e5aaf047SAppaRao Puli             {"Protocol", "Redfish"}};
437e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["@odata.id"] =
438e5aaf047SAppaRao Puli             "/redfish/v1/EventService/Subscriptions/" + id;
439e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Id"] = id;
440e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
441b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
442b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Context"] = subValue->customText;
443e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["SubscriptionType"] =
444b52664e2SAppaRao Puli             subValue->subscriptionType;
445b52664e2SAppaRao Puli         asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
446b52664e2SAppaRao Puli         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
447e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] =
448b52664e2SAppaRao Puli             subValue->registryPrefixes;
449b52664e2SAppaRao Puli         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
450b52664e2SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
451*156d6b00SAppaRao Puli         asyncResp->res.jsonValue["MetricReportDefinitions"] =
452*156d6b00SAppaRao Puli             subValue->metricReportDefinitions;
453e5aaf047SAppaRao Puli     }
454e5aaf047SAppaRao Puli 
455e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
456e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
457e5aaf047SAppaRao Puli     {
458e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
459e5aaf047SAppaRao Puli         if (params.size() != 1)
460e5aaf047SAppaRao Puli         {
461e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
462e5aaf047SAppaRao Puli             return;
463e5aaf047SAppaRao Puli         }
464e5aaf047SAppaRao Puli 
465b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
466b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
467b52664e2SAppaRao Puli         if (subValue == nullptr)
468e5aaf047SAppaRao Puli         {
469e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
470e5aaf047SAppaRao Puli             res.end();
471e5aaf047SAppaRao Puli             return;
472e5aaf047SAppaRao Puli         }
473e5aaf047SAppaRao Puli 
474e5aaf047SAppaRao Puli         std::optional<std::string> context;
475e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
476e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
477e5aaf047SAppaRao Puli 
478e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "Context", context,
479e5aaf047SAppaRao Puli                                  "DeliveryRetryPolicy", retryPolicy,
480e5aaf047SAppaRao Puli                                  "HttpHeaders", headers))
481e5aaf047SAppaRao Puli         {
482e5aaf047SAppaRao Puli             return;
483e5aaf047SAppaRao Puli         }
484e5aaf047SAppaRao Puli 
485e5aaf047SAppaRao Puli         if (context)
486e5aaf047SAppaRao Puli         {
487b52664e2SAppaRao Puli             subValue->customText = *context;
488e5aaf047SAppaRao Puli         }
489e5aaf047SAppaRao Puli 
490e5aaf047SAppaRao Puli         if (headers)
491e5aaf047SAppaRao Puli         {
492b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
493e5aaf047SAppaRao Puli         }
494e5aaf047SAppaRao Puli 
495e5aaf047SAppaRao Puli         if (retryPolicy)
496e5aaf047SAppaRao Puli         {
497e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
498e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
499e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
500e5aaf047SAppaRao Puli             {
501e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
502e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
503e5aaf047SAppaRao Puli                 return;
504e5aaf047SAppaRao Puli             }
505b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
506e5aaf047SAppaRao Puli         }
507e5aaf047SAppaRao Puli 
508b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
509e5aaf047SAppaRao Puli     }
510e5aaf047SAppaRao Puli 
511e5aaf047SAppaRao Puli     void doDelete(crow::Response& res, const crow::Request& req,
512e5aaf047SAppaRao Puli                   const std::vector<std::string>& params) override
513e5aaf047SAppaRao Puli     {
514e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
515e5aaf047SAppaRao Puli 
516e5aaf047SAppaRao Puli         if (params.size() != 1)
517e5aaf047SAppaRao Puli         {
518e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
519e5aaf047SAppaRao Puli             return;
520e5aaf047SAppaRao Puli         }
521e5aaf047SAppaRao Puli 
522b52664e2SAppaRao Puli         if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
523e5aaf047SAppaRao Puli         {
524e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
525e5aaf047SAppaRao Puli             res.end();
526e5aaf047SAppaRao Puli             return;
527e5aaf047SAppaRao Puli         }
528b52664e2SAppaRao Puli         EventServiceManager::getInstance().deleteSubscription(params[0]);
529e5aaf047SAppaRao Puli     }
530e5aaf047SAppaRao Puli };
531e5aaf047SAppaRao Puli 
532e5aaf047SAppaRao Puli } // namespace redfish
533