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