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     std::string id;
16     boost::urls::url destinationUrl;
17     std::string protocol;
18     bool verifyCertificate = true;
19     std::string retryPolicy;
20     std::string customText;
21     std::string eventFormatType;
22     std::string subscriptionType;
23     std::vector<std::string> registryMsgIds;
24     std::vector<std::string> registryPrefixes;
25     std::vector<std::string> resourceTypes;
26     boost::beast::http::fields httpHeaders;
27     std::vector<std::string> metricReportDefinitions;
28     std::vector<std::string> originResources;
29 
30     static std::shared_ptr<UserSubscription> fromJson(
31         const nlohmann::json::object_t& j, const bool loadFromOldConfig = false)
32     {
33         std::shared_ptr<UserSubscription> subvalue =
34             std::make_shared<UserSubscription>();
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() ||
250             subvalue->eventFormatType.empty() ||
251             subvalue->subscriptionType.empty())
252         {
253             BMCWEB_LOG_ERROR("Subscription missing required field "
254                              "information, refusing to restore");
255             return nullptr;
256         }
257 
258         return subvalue;
259     }
260 };
261 
262 struct EventServiceConfig
263 {
264     bool enabled = true;
265     uint32_t retryAttempts = 3;
266     uint32_t retryTimeoutInterval = 30;
267 
268     void fromJson(const nlohmann::json::object_t& j)
269     {
270         for (const auto& element : j)
271         {
272             if (element.first == "ServiceEnabled")
273             {
274                 const bool* value = element.second.get_ptr<const bool*>();
275                 if (value == nullptr)
276                 {
277                     continue;
278                 }
279                 enabled = *value;
280             }
281             else if (element.first == "DeliveryRetryAttempts")
282             {
283                 const uint64_t* value =
284                     element.second.get_ptr<const uint64_t*>();
285                 if ((value == nullptr) ||
286                     (*value > std::numeric_limits<uint32_t>::max()))
287                 {
288                     continue;
289                 }
290                 retryAttempts = static_cast<uint32_t>(*value);
291             }
292             else if (element.first == "DeliveryRetryIntervalSeconds")
293             {
294                 const uint64_t* value =
295                     element.second.get_ptr<const uint64_t*>();
296                 if ((value == nullptr) ||
297                     (*value > std::numeric_limits<uint32_t>::max()))
298                 {
299                     continue;
300                 }
301                 retryTimeoutInterval = static_cast<uint32_t>(*value);
302             }
303         }
304     }
305 };
306 
307 class EventServiceStore
308 {
309   public:
310     boost::container::flat_map<std::string, std::shared_ptr<UserSubscription>>
311         subscriptionsConfigMap;
312     EventServiceConfig eventServiceConfig;
313 
314     static EventServiceStore& getInstance()
315     {
316         static EventServiceStore eventServiceStore;
317         return eventServiceStore;
318     }
319 
320     EventServiceConfig& getEventServiceConfig()
321     {
322         return eventServiceConfig;
323     }
324 };
325 
326 } // namespace persistent_data
327