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