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