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