xref: /openbmc/bmcweb/include/event_service_store.hpp (revision 56b81992ba8a8e644f2e75251a94df4f4d0d0880)
1 #pragma once
2 #include "logging.hpp"
3 
4 #include <boost/beast/http/fields.hpp>
5 #include <boost/container/flat_map.hpp>
6 #include <boost/url/parse.hpp>
7 #include <boost/url/url.hpp>
8 #include <nlohmann/json.hpp>
9 
10 #include <limits>
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 namespace persistent_data
16 {
17 
18 struct UserSubscription
19 {
20     // Represents a Redfish EventDestination instance
21     std::string id;
22     boost::urls::url destinationUrl;
23     std::string protocol;
24     bool verifyCertificate = true;
25     std::string retryPolicy;
26     bool sendHeartbeat = false;
27     // This value of hbIntervalMinutes is just a reasonable default value and
28     // most clients will update it if sendHeartbeat is turned on
29     uint64_t hbIntervalMinutes = 10;
30     std::string customText;
31     std::string eventFormatType;
32     std::string subscriptionType;
33     std::vector<std::string> registryMsgIds;
34     std::vector<std::string> registryPrefixes;
35     std::vector<std::string> resourceTypes;
36     boost::beast::http::fields httpHeaders;
37     std::vector<std::string> metricReportDefinitions;
38     std::vector<std::string> originResources;
39 
40     static std::optional<UserSubscription> fromJson(
41         const nlohmann::json::object_t& j, const bool loadFromOldConfig = false)
42     {
43         UserSubscription subvalue;
44         for (const auto& element : j)
45         {
46             if (element.first == "Id")
47             {
48                 const std::string* value =
49                     element.second.get_ptr<const std::string*>();
50                 if (value == nullptr)
51                 {
52                     continue;
53                 }
54                 subvalue.id = *value;
55             }
56             else if (element.first == "Destination")
57             {
58                 const std::string* value =
59                     element.second.get_ptr<const std::string*>();
60                 if (value == nullptr)
61                 {
62                     continue;
63                 }
64                 boost::system::result<boost::urls::url> url =
65                     boost::urls::parse_absolute_uri(*value);
66                 if (!url)
67                 {
68                     continue;
69                 }
70                 subvalue.destinationUrl = std::move(*url);
71             }
72             else if (element.first == "Protocol")
73             {
74                 const std::string* value =
75                     element.second.get_ptr<const std::string*>();
76                 if (value == nullptr)
77                 {
78                     continue;
79                 }
80                 subvalue.protocol = *value;
81             }
82             else if (element.first == "VerifyCertificate")
83             {
84                 const bool* value = element.second.get_ptr<const bool*>();
85                 if (value == nullptr)
86                 {
87                     continue;
88                 }
89                 subvalue.verifyCertificate = *value;
90             }
91             else if (element.first == "DeliveryRetryPolicy")
92             {
93                 const std::string* value =
94                     element.second.get_ptr<const std::string*>();
95                 if (value == nullptr)
96                 {
97                     continue;
98                 }
99                 subvalue.retryPolicy = *value;
100             }
101             else if (element.first == "SendHeartbeat")
102             {
103                 const bool* value = element.second.get_ptr<const bool*>();
104                 if (value == nullptr)
105                 {
106                     continue;
107                 }
108                 subvalue.sendHeartbeat = *value;
109             }
110             else if (element.first == "HeartbeatIntervalMinutes")
111             {
112                 const uint64_t* value =
113                     element.second.get_ptr<const uint64_t*>();
114                 if (value == nullptr || *value < 1 || *value > 65535)
115                 {
116                     continue;
117                 }
118                 subvalue.hbIntervalMinutes = *value;
119             }
120             else if (element.first == "Context")
121             {
122                 const std::string* value =
123                     element.second.get_ptr<const std::string*>();
124                 if (value == nullptr)
125                 {
126                     continue;
127                 }
128                 subvalue.customText = *value;
129             }
130             else if (element.first == "EventFormatType")
131             {
132                 const std::string* value =
133                     element.second.get_ptr<const std::string*>();
134                 if (value == nullptr)
135                 {
136                     continue;
137                 }
138                 subvalue.eventFormatType = *value;
139             }
140             else if (element.first == "SubscriptionType")
141             {
142                 const std::string* value =
143                     element.second.get_ptr<const std::string*>();
144                 if (value == nullptr)
145                 {
146                     continue;
147                 }
148                 subvalue.subscriptionType = *value;
149             }
150             else if (element.first == "MessageIds")
151             {
152                 const nlohmann::json::array_t* obj =
153                     element.second.get_ptr<const nlohmann::json::array_t*>();
154                 if (obj == nullptr)
155                 {
156                     continue;
157                 }
158                 for (const auto& val : *obj)
159                 {
160                     const std::string* value =
161                         val.get_ptr<const std::string*>();
162                     if (value == nullptr)
163                     {
164                         continue;
165                     }
166                     subvalue.registryMsgIds.emplace_back(*value);
167                 }
168             }
169             else if (element.first == "RegistryPrefixes")
170             {
171                 const nlohmann::json::array_t* obj =
172                     element.second.get_ptr<const nlohmann::json::array_t*>();
173                 if (obj == nullptr)
174                 {
175                     continue;
176                 }
177                 for (const auto& val : *obj)
178                 {
179                     const std::string* value =
180                         val.get_ptr<const std::string*>();
181                     if (value == nullptr)
182                     {
183                         continue;
184                     }
185                     subvalue.registryPrefixes.emplace_back(*value);
186                 }
187             }
188             else if (element.first == "ResourceTypes")
189             {
190                 const nlohmann::json::array_t* obj =
191                     element.second.get_ptr<const nlohmann::json::array_t*>();
192                 if (obj == nullptr)
193                 {
194                     continue;
195                 }
196                 for (const auto& val : *obj)
197                 {
198                     const std::string* value =
199                         val.get_ptr<const std::string*>();
200                     if (value == nullptr)
201                     {
202                         continue;
203                     }
204                     subvalue.resourceTypes.emplace_back(*value);
205                 }
206             }
207             else if (element.first == "HttpHeaders")
208             {
209                 const nlohmann::json::object_t* obj =
210                     element.second.get_ptr<const nlohmann::json::object_t*>();
211                 if (obj == nullptr)
212                 {
213                     continue;
214                 }
215                 for (const auto& val : *obj)
216                 {
217                     const std::string* value =
218                         val.second.get_ptr<const std::string*>();
219                     if (value == nullptr)
220                     {
221                         BMCWEB_LOG_ERROR("Failed to parse value for key{}",
222                                          val.first);
223                         continue;
224                     }
225                     subvalue.httpHeaders.set(val.first, *value);
226                 }
227             }
228             else if (element.first == "MetricReportDefinitions")
229             {
230                 const nlohmann::json::array_t* obj =
231                     element.second.get_ptr<const nlohmann::json::array_t*>();
232                 if (obj == nullptr)
233                 {
234                     continue;
235                 }
236                 for (const auto& val : *obj)
237                 {
238                     const std::string* value =
239                         val.get_ptr<const std::string*>();
240                     if (value == nullptr)
241                     {
242                         continue;
243                     }
244                     subvalue.metricReportDefinitions.emplace_back(*value);
245                 }
246             }
247             else if (element.first == "OriginResources")
248             {
249                 const nlohmann::json::array_t* obj =
250                     element.second.get_ptr<const nlohmann::json::array_t*>();
251                 if (obj == nullptr)
252                 {
253                     continue;
254                 }
255                 for (const auto& val : *obj)
256                 {
257                     const std::string* value =
258                         val.get_ptr<const std::string*>();
259                     if (value == nullptr)
260                     {
261                         continue;
262                     }
263                     subvalue.originResources.emplace_back(*value);
264                 }
265             }
266             else
267             {
268                 BMCWEB_LOG_ERROR(
269                     "Got unexpected property reading persistent file: {}",
270                     element.first);
271                 continue;
272             }
273         }
274 
275         if ((subvalue.id.empty() && !loadFromOldConfig) ||
276             subvalue.destinationUrl.empty() || subvalue.protocol.empty() ||
277             subvalue.retryPolicy.empty() || subvalue.eventFormatType.empty() ||
278             subvalue.subscriptionType.empty())
279         {
280             BMCWEB_LOG_ERROR("Subscription missing required field "
281                              "information, refusing to restore");
282             return std::nullopt;
283         }
284 
285         return subvalue;
286     }
287 };
288 
289 struct EventServiceConfig
290 {
291     bool enabled = true;
292     uint32_t retryAttempts = 3;
293     uint32_t retryTimeoutInterval = 30;
294 
295     void fromJson(const nlohmann::json::object_t& j)
296     {
297         for (const auto& element : j)
298         {
299             if (element.first == "ServiceEnabled")
300             {
301                 const bool* value = element.second.get_ptr<const bool*>();
302                 if (value == nullptr)
303                 {
304                     continue;
305                 }
306                 enabled = *value;
307             }
308             else if (element.first == "DeliveryRetryAttempts")
309             {
310                 const uint64_t* value =
311                     element.second.get_ptr<const uint64_t*>();
312                 if ((value == nullptr) ||
313                     (*value > std::numeric_limits<uint32_t>::max()))
314                 {
315                     continue;
316                 }
317                 retryAttempts = static_cast<uint32_t>(*value);
318             }
319             else if (element.first == "DeliveryRetryIntervalSeconds")
320             {
321                 const uint64_t* value =
322                     element.second.get_ptr<const uint64_t*>();
323                 if ((value == nullptr) ||
324                     (*value > std::numeric_limits<uint32_t>::max()))
325                 {
326                     continue;
327                 }
328                 retryTimeoutInterval = static_cast<uint32_t>(*value);
329             }
330         }
331     }
332 };
333 
334 class EventServiceStore
335 {
336   public:
337     boost::container::flat_map<std::string, std::shared_ptr<UserSubscription>>
338         subscriptionsConfigMap;
339     EventServiceConfig eventServiceConfig;
340 
341     static EventServiceStore& getInstance()
342     {
343         static EventServiceStore eventServiceStore;
344         return eventServiceStore;
345     }
346 
347     EventServiceConfig& getEventServiceConfig()
348     {
349         return eventServiceConfig;
350     }
351 };
352 
353 } // namespace persistent_data
354