xref: /openbmc/bmcweb/features/redfish/lib/event_service.hpp (revision 0b4bdd93c79779913fcfc3641beb5f7f3966f339)
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 
22e5aaf047SAppaRao Puli static constexpr const std::array<const char*, 1> supportedEvtFormatTypes = {
23e5aaf047SAppaRao Puli     "Event"};
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"}}},
58*0b4bdd93SAppaRao Puli             {"Actions",
59*0b4bdd93SAppaRao Puli              {{"#EventService.SubmitTestEvent",
60*0b4bdd93SAppaRao Puli                {{"target", "/redfish/v1/EventService/Actions/"
61*0b4bdd93SAppaRao 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 
132*0b4bdd93SAppaRao Puli class SubmitTestEvent : public Node
133*0b4bdd93SAppaRao Puli {
134*0b4bdd93SAppaRao Puli   public:
135*0b4bdd93SAppaRao Puli     SubmitTestEvent(CrowApp& app) :
136*0b4bdd93SAppaRao Puli         Node(app,
137*0b4bdd93SAppaRao Puli              "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent/")
138*0b4bdd93SAppaRao Puli     {
139*0b4bdd93SAppaRao Puli         entityPrivileges = {
140*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
141*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
142*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
143*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
144*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
145*0b4bdd93SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
146*0b4bdd93SAppaRao Puli     }
147*0b4bdd93SAppaRao Puli 
148*0b4bdd93SAppaRao Puli   private:
149*0b4bdd93SAppaRao Puli     void doPost(crow::Response& res, const crow::Request& req,
150*0b4bdd93SAppaRao Puli                 const std::vector<std::string>& params) override
151*0b4bdd93SAppaRao Puli     {
152*0b4bdd93SAppaRao Puli         EventServiceManager::getInstance().sendTestEventLog();
153*0b4bdd93SAppaRao Puli         res.result(boost::beast::http::status::no_content);
154*0b4bdd93SAppaRao Puli         res.end();
155*0b4bdd93SAppaRao Puli     }
156*0b4bdd93SAppaRao Puli };
157*0b4bdd93SAppaRao 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;
220e5aaf047SAppaRao Puli 
221e5aaf047SAppaRao Puli         if (!json_util::readJson(
222e5aaf047SAppaRao Puli                 req, res, "Destination", destUrl, "Context", context,
223e5aaf047SAppaRao Puli                 "Protocol", protocol, "SubscriptionType", subscriptionType,
224e5aaf047SAppaRao Puli                 "EventFormatType", eventFormatType, "HttpHeaders", headers,
225e5aaf047SAppaRao Puli                 "RegistryPrefixes", regPrefixes, "MessageIds", msgIds,
226e5aaf047SAppaRao Puli                 "DeliveryRetryPolicy", retryPolicy))
227e5aaf047SAppaRao Puli         {
228e5aaf047SAppaRao Puli             return;
229e5aaf047SAppaRao Puli         }
230e5aaf047SAppaRao Puli 
231e5aaf047SAppaRao Puli         // Validate the URL using regex expression
232b52664e2SAppaRao Puli         // Format: <protocol>://<host>:<port>/<uri>
233b52664e2SAppaRao Puli         // protocol: http/https
234b52664e2SAppaRao Puli         // host: Exclude ' ', ':', '#', '?'
235b52664e2SAppaRao Puli         // port: Empty or numeric value with ':' seperator.
236b52664e2SAppaRao Puli         // uri: Start with '/' and Exclude '#', ' '
237b52664e2SAppaRao Puli         //      Can include query params(ex: '/event?test=1')
238b52664e2SAppaRao Puli         // TODO: Need to validate hostname extensively(as per rfc)
239b52664e2SAppaRao Puli         const std::regex urlRegex(
240b52664e2SAppaRao Puli             "(http|https)://([^/\\x20\\x3f\\x23\\x3a]+):?([0-9]*)(/"
241b52664e2SAppaRao Puli             "([^\\x20\\x23\\x3f]*\\x3f?([^\\x20\\x23\\x3f])*)?)");
242b52664e2SAppaRao Puli         std::cmatch match;
243b52664e2SAppaRao Puli         if (!std::regex_match(destUrl.c_str(), match, urlRegex))
244e5aaf047SAppaRao Puli         {
245e5aaf047SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
246e5aaf047SAppaRao Puli                                                "Destination");
247e5aaf047SAppaRao Puli             return;
248e5aaf047SAppaRao Puli         }
249b52664e2SAppaRao Puli 
250b52664e2SAppaRao Puli         std::string uriProto = std::string(match[1].first, match[1].second);
251b52664e2SAppaRao Puli         if (uriProto == "http")
252b52664e2SAppaRao Puli         {
253b52664e2SAppaRao Puli #ifndef BMCWEB_INSECURE_ENABLE_HTTP_PUSH_STYLE_EVENTING
254b52664e2SAppaRao Puli             messages::propertyValueFormatError(asyncResp->res, destUrl,
255b52664e2SAppaRao Puli                                                "Destination");
256b52664e2SAppaRao Puli             return;
257b52664e2SAppaRao Puli #endif
258b52664e2SAppaRao Puli         }
259b52664e2SAppaRao Puli 
260b52664e2SAppaRao Puli         std::string host = std::string(match[2].first, match[2].second);
261b52664e2SAppaRao Puli         std::string port = std::string(match[3].first, match[3].second);
262b52664e2SAppaRao Puli         std::string path = std::string(match[4].first, match[4].second);
263b52664e2SAppaRao Puli         if (port.empty())
264b52664e2SAppaRao Puli         {
265b52664e2SAppaRao Puli             if (uriProto == "http")
266b52664e2SAppaRao Puli             {
267b52664e2SAppaRao Puli                 port = "80";
268b52664e2SAppaRao Puli             }
269b52664e2SAppaRao Puli             else
270b52664e2SAppaRao Puli             {
271b52664e2SAppaRao Puli                 port = "443";
272b52664e2SAppaRao Puli             }
273b52664e2SAppaRao Puli         }
274b52664e2SAppaRao Puli         if (path.empty())
275b52664e2SAppaRao Puli         {
276b52664e2SAppaRao Puli             path = "/";
277b52664e2SAppaRao Puli         }
278b52664e2SAppaRao Puli 
279b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
280b52664e2SAppaRao Puli             std::make_shared<Subscription>(host, port, path, uriProto);
281b52664e2SAppaRao Puli 
282b52664e2SAppaRao Puli         subValue->destinationUrl = destUrl;
283e5aaf047SAppaRao Puli 
284e5aaf047SAppaRao Puli         if (subscriptionType)
285e5aaf047SAppaRao Puli         {
286e5aaf047SAppaRao Puli             if (*subscriptionType != "RedfishEvent")
287e5aaf047SAppaRao Puli             {
288e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
289e5aaf047SAppaRao Puli                     asyncResp->res, *subscriptionType, "SubscriptionType");
290e5aaf047SAppaRao Puli                 return;
291e5aaf047SAppaRao Puli             }
292b52664e2SAppaRao Puli             subValue->subscriptionType = *subscriptionType;
293e5aaf047SAppaRao Puli         }
294e5aaf047SAppaRao Puli         else
295e5aaf047SAppaRao Puli         {
296b52664e2SAppaRao Puli             subValue->subscriptionType = "RedfishEvent"; // Default
297e5aaf047SAppaRao Puli         }
298e5aaf047SAppaRao Puli 
299e5aaf047SAppaRao Puli         if (protocol != "Redfish")
300e5aaf047SAppaRao Puli         {
301e5aaf047SAppaRao Puli             messages::propertyValueNotInList(asyncResp->res, protocol,
302e5aaf047SAppaRao Puli                                              "Protocol");
303e5aaf047SAppaRao Puli             return;
304e5aaf047SAppaRao Puli         }
305b52664e2SAppaRao Puli         subValue->protocol = protocol;
306e5aaf047SAppaRao Puli 
307e5aaf047SAppaRao Puli         if (eventFormatType)
308e5aaf047SAppaRao Puli         {
309e5aaf047SAppaRao Puli             if (std::find(supportedEvtFormatTypes.begin(),
310e5aaf047SAppaRao Puli                           supportedEvtFormatTypes.end(),
311e5aaf047SAppaRao Puli                           *eventFormatType) == supportedEvtFormatTypes.end())
312e5aaf047SAppaRao Puli             {
313e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(
314e5aaf047SAppaRao Puli                     asyncResp->res, *eventFormatType, "EventFormatType");
315e5aaf047SAppaRao Puli                 return;
316e5aaf047SAppaRao Puli             }
317b52664e2SAppaRao Puli             subValue->eventFormatType = *eventFormatType;
318e5aaf047SAppaRao Puli         }
319e5aaf047SAppaRao Puli         else
320e5aaf047SAppaRao Puli         {
321e5aaf047SAppaRao Puli             // If not specified, use default "Event"
322b52664e2SAppaRao Puli             subValue->eventFormatType.assign({"Event"});
323e5aaf047SAppaRao Puli         }
324e5aaf047SAppaRao Puli 
325e5aaf047SAppaRao Puli         if (context)
326e5aaf047SAppaRao Puli         {
327b52664e2SAppaRao Puli             subValue->customText = *context;
328e5aaf047SAppaRao Puli         }
329e5aaf047SAppaRao Puli 
330e5aaf047SAppaRao Puli         if (headers)
331e5aaf047SAppaRao Puli         {
332b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
333e5aaf047SAppaRao Puli         }
334e5aaf047SAppaRao Puli 
335e5aaf047SAppaRao Puli         if (regPrefixes)
336e5aaf047SAppaRao Puli         {
337e5aaf047SAppaRao Puli             for (const std::string& it : *regPrefixes)
338e5aaf047SAppaRao Puli             {
339e5aaf047SAppaRao Puli                 if (std::find(supportedRegPrefixes.begin(),
340e5aaf047SAppaRao Puli                               supportedRegPrefixes.end(),
341e5aaf047SAppaRao Puli                               it) == supportedRegPrefixes.end())
342e5aaf047SAppaRao Puli                 {
343e5aaf047SAppaRao Puli                     messages::propertyValueNotInList(asyncResp->res, it,
344e5aaf047SAppaRao Puli                                                      "RegistryPrefixes");
345e5aaf047SAppaRao Puli                     return;
346e5aaf047SAppaRao Puli                 }
347e5aaf047SAppaRao Puli             }
348b52664e2SAppaRao Puli             subValue->registryPrefixes = *regPrefixes;
349e5aaf047SAppaRao Puli         }
350e5aaf047SAppaRao Puli 
351e5aaf047SAppaRao Puli         if (msgIds)
352e5aaf047SAppaRao Puli         {
353e5aaf047SAppaRao Puli             // Do we need to loop-up MessageRegistry and validate
354e5aaf047SAppaRao Puli             // data for authenticity??? Not mandate, i believe.
355b52664e2SAppaRao Puli             subValue->registryMsgIds = *msgIds;
356e5aaf047SAppaRao Puli         }
357e5aaf047SAppaRao Puli 
358e5aaf047SAppaRao Puli         if (retryPolicy)
359e5aaf047SAppaRao Puli         {
360e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
361e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
362e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
363e5aaf047SAppaRao Puli             {
364e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
365e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
366e5aaf047SAppaRao Puli                 return;
367e5aaf047SAppaRao Puli             }
368b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
369e5aaf047SAppaRao Puli         }
370e5aaf047SAppaRao Puli         else
371e5aaf047SAppaRao Puli         {
372e5aaf047SAppaRao Puli             // Default "TerminateAfterRetries"
373b52664e2SAppaRao Puli             subValue->retryPolicy = "TerminateAfterRetries";
374e5aaf047SAppaRao Puli         }
375e5aaf047SAppaRao Puli 
376b52664e2SAppaRao Puli         std::string id =
377b52664e2SAppaRao Puli             EventServiceManager::getInstance().addSubscription(subValue);
378b52664e2SAppaRao Puli         if (id.empty())
379e5aaf047SAppaRao Puli         {
380e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
381e5aaf047SAppaRao Puli             return;
382e5aaf047SAppaRao Puli         }
383e5aaf047SAppaRao Puli 
384e5aaf047SAppaRao Puli         messages::created(asyncResp->res);
385e5aaf047SAppaRao Puli         asyncResp->res.addHeader(
386e5aaf047SAppaRao Puli             "Location", "/redfish/v1/EventService/Subscriptions/" + id);
387e5aaf047SAppaRao Puli     }
388e5aaf047SAppaRao Puli };
389e5aaf047SAppaRao Puli 
390e5aaf047SAppaRao Puli class EventDestination : public Node
391e5aaf047SAppaRao Puli {
392e5aaf047SAppaRao Puli   public:
393e5aaf047SAppaRao Puli     EventDestination(CrowApp& app) :
394e5aaf047SAppaRao Puli         Node(app, "/redfish/v1/EventService/Subscriptions/<str>/",
395e5aaf047SAppaRao Puli              std::string())
396e5aaf047SAppaRao Puli     {
397e5aaf047SAppaRao Puli         entityPrivileges = {
398e5aaf047SAppaRao Puli             {boost::beast::http::verb::get, {{"Login"}}},
399e5aaf047SAppaRao Puli             {boost::beast::http::verb::head, {{"Login"}}},
400e5aaf047SAppaRao Puli             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
401e5aaf047SAppaRao Puli             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
402e5aaf047SAppaRao Puli             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
403e5aaf047SAppaRao Puli             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
404e5aaf047SAppaRao Puli     }
405e5aaf047SAppaRao Puli 
406e5aaf047SAppaRao Puli   private:
407e5aaf047SAppaRao Puli     void doGet(crow::Response& res, const crow::Request& req,
408e5aaf047SAppaRao Puli                const std::vector<std::string>& params) override
409e5aaf047SAppaRao Puli     {
410e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
411e5aaf047SAppaRao Puli         if (params.size() != 1)
412e5aaf047SAppaRao Puli         {
413e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
414e5aaf047SAppaRao Puli             return;
415e5aaf047SAppaRao Puli         }
416e5aaf047SAppaRao Puli 
417b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
418b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
419b52664e2SAppaRao Puli         if (subValue == nullptr)
420e5aaf047SAppaRao Puli         {
421e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
422e5aaf047SAppaRao Puli             res.end();
423e5aaf047SAppaRao Puli             return;
424e5aaf047SAppaRao Puli         }
425b52664e2SAppaRao Puli         const std::string& id = params[0];
426e5aaf047SAppaRao Puli 
427e5aaf047SAppaRao Puli         res.jsonValue = {
428e5aaf047SAppaRao Puli             {"@odata.type", "#EventDestination.v1_7_0.EventDestination"},
429e5aaf047SAppaRao Puli             {"Protocol", "Redfish"}};
430e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["@odata.id"] =
431e5aaf047SAppaRao Puli             "/redfish/v1/EventService/Subscriptions/" + id;
432e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Id"] = id;
433e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["Name"] = "Event Destination " + id;
434b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Destination"] = subValue->destinationUrl;
435b52664e2SAppaRao Puli         asyncResp->res.jsonValue["Context"] = subValue->customText;
436e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["SubscriptionType"] =
437b52664e2SAppaRao Puli             subValue->subscriptionType;
438b52664e2SAppaRao Puli         asyncResp->res.jsonValue["HttpHeaders"] = subValue->httpHeaders;
439b52664e2SAppaRao Puli         asyncResp->res.jsonValue["EventFormatType"] = subValue->eventFormatType;
440e5aaf047SAppaRao Puli         asyncResp->res.jsonValue["RegistryPrefixes"] =
441b52664e2SAppaRao Puli             subValue->registryPrefixes;
442b52664e2SAppaRao Puli         asyncResp->res.jsonValue["MessageIds"] = subValue->registryMsgIds;
443b52664e2SAppaRao Puli         asyncResp->res.jsonValue["DeliveryRetryPolicy"] = subValue->retryPolicy;
444e5aaf047SAppaRao Puli     }
445e5aaf047SAppaRao Puli 
446e5aaf047SAppaRao Puli     void doPatch(crow::Response& res, const crow::Request& req,
447e5aaf047SAppaRao Puli                  const std::vector<std::string>& params) override
448e5aaf047SAppaRao Puli     {
449e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
450e5aaf047SAppaRao Puli         if (params.size() != 1)
451e5aaf047SAppaRao Puli         {
452e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
453e5aaf047SAppaRao Puli             return;
454e5aaf047SAppaRao Puli         }
455e5aaf047SAppaRao Puli 
456b52664e2SAppaRao Puli         std::shared_ptr<Subscription> subValue =
457b52664e2SAppaRao Puli             EventServiceManager::getInstance().getSubscription(params[0]);
458b52664e2SAppaRao Puli         if (subValue == nullptr)
459e5aaf047SAppaRao Puli         {
460e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
461e5aaf047SAppaRao Puli             res.end();
462e5aaf047SAppaRao Puli             return;
463e5aaf047SAppaRao Puli         }
464e5aaf047SAppaRao Puli 
465e5aaf047SAppaRao Puli         std::optional<std::string> context;
466e5aaf047SAppaRao Puli         std::optional<std::string> retryPolicy;
467e5aaf047SAppaRao Puli         std::optional<std::vector<nlohmann::json>> headers;
468e5aaf047SAppaRao Puli 
469e5aaf047SAppaRao Puli         if (!json_util::readJson(req, res, "Context", context,
470e5aaf047SAppaRao Puli                                  "DeliveryRetryPolicy", retryPolicy,
471e5aaf047SAppaRao Puli                                  "HttpHeaders", headers))
472e5aaf047SAppaRao Puli         {
473e5aaf047SAppaRao Puli             return;
474e5aaf047SAppaRao Puli         }
475e5aaf047SAppaRao Puli 
476e5aaf047SAppaRao Puli         if (context)
477e5aaf047SAppaRao Puli         {
478b52664e2SAppaRao Puli             subValue->customText = *context;
479e5aaf047SAppaRao Puli         }
480e5aaf047SAppaRao Puli 
481e5aaf047SAppaRao Puli         if (headers)
482e5aaf047SAppaRao Puli         {
483b52664e2SAppaRao Puli             subValue->httpHeaders = *headers;
484e5aaf047SAppaRao Puli         }
485e5aaf047SAppaRao Puli 
486e5aaf047SAppaRao Puli         if (retryPolicy)
487e5aaf047SAppaRao Puli         {
488e5aaf047SAppaRao Puli             if (std::find(supportedRetryPolicies.begin(),
489e5aaf047SAppaRao Puli                           supportedRetryPolicies.end(),
490e5aaf047SAppaRao Puli                           *retryPolicy) == supportedRetryPolicies.end())
491e5aaf047SAppaRao Puli             {
492e5aaf047SAppaRao Puli                 messages::propertyValueNotInList(asyncResp->res, *retryPolicy,
493e5aaf047SAppaRao Puli                                                  "DeliveryRetryPolicy");
494e5aaf047SAppaRao Puli                 return;
495e5aaf047SAppaRao Puli             }
496b52664e2SAppaRao Puli             subValue->retryPolicy = *retryPolicy;
497e5aaf047SAppaRao Puli         }
498e5aaf047SAppaRao Puli 
499b52664e2SAppaRao Puli         EventServiceManager::getInstance().updateSubscriptionData();
500e5aaf047SAppaRao Puli     }
501e5aaf047SAppaRao Puli 
502e5aaf047SAppaRao Puli     void doDelete(crow::Response& res, const crow::Request& req,
503e5aaf047SAppaRao Puli                   const std::vector<std::string>& params) override
504e5aaf047SAppaRao Puli     {
505e5aaf047SAppaRao Puli         auto asyncResp = std::make_shared<AsyncResp>(res);
506e5aaf047SAppaRao Puli 
507e5aaf047SAppaRao Puli         if (params.size() != 1)
508e5aaf047SAppaRao Puli         {
509e5aaf047SAppaRao Puli             messages::internalError(asyncResp->res);
510e5aaf047SAppaRao Puli             return;
511e5aaf047SAppaRao Puli         }
512e5aaf047SAppaRao Puli 
513b52664e2SAppaRao Puli         if (!EventServiceManager::getInstance().isSubscriptionExist(params[0]))
514e5aaf047SAppaRao Puli         {
515e5aaf047SAppaRao Puli             res.result(boost::beast::http::status::not_found);
516e5aaf047SAppaRao Puli             res.end();
517e5aaf047SAppaRao Puli             return;
518e5aaf047SAppaRao Puli         }
519b52664e2SAppaRao Puli         EventServiceManager::getInstance().deleteSubscription(params[0]);
520e5aaf047SAppaRao Puli     }
521e5aaf047SAppaRao Puli };
522e5aaf047SAppaRao Puli 
523e5aaf047SAppaRao Puli } // namespace redfish
524