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