Modern C++ Kafka API
Properties.h
1 #pragma once
2 
3 #include <kafka/Project.h>
4 
5 #include <kafka/ClientCommon.h>
6 #include <kafka/Error.h>
7 #include <kafka/Interceptors.h>
8 #include <kafka/KafkaException.h>
9 #include <kafka/Types.h>
10 
11 #include <algorithm>
12 #include <map>
13 #include <regex>
14 #include <string>
15 #include <typeinfo>
16 
17 
18 namespace KAFKA_API {
19 
24 {
25 private:
26  using LogCallback = clients::LogCallback;
27  using ErrorCallback = clients::ErrorCallback;
28  using StatsCallback = clients::StatsCallback;
29  using OauthbearerTokenRefreshCallback = clients::OauthbearerTokenRefreshCallback;
31 
32  struct ValueType
33  {
34  struct Object
35  {
36  virtual ~Object() = default;
37  virtual std::string toString() const = 0;
38  };
39 
40  template<class T>
41  static std::string getString(const T& /*value*/) { return typeid(T).name(); }
42  template<class T>
43  static std::string getString(const std::string& value) { return value; }
44 
45  const ValueType& validate(const std::string& key) const
46  {
47  static const std::vector<std::string> nonStringValueKeys = {
48  "log_cb", "error_cb", "stats_cb", "oauthbearer_token_refresh_cb", "interceptors"
49  };
50 
51  if ((expectedKey.empty() && std::any_of(nonStringValueKeys.cbegin(), nonStringValueKeys.cend(), [key](const auto& k) { return k == key; }))
52  || (!expectedKey.empty() && key != expectedKey))
53  {
54  throw std::runtime_error("Invalid key/value for configuration: " + key);
55  }
56 
57  return *this;
58  }
59 
60  template<class T>
61  struct ObjWrap: public Object
62  {
63  explicit ObjWrap(T v): value(std::move(v)) {}
64  std::string toString() const override { return getString<T>(value); }
65  T value;
66  };
67 
68  template<class T>
69  T& getValue() const { return (dynamic_cast<ObjWrap<T>&>(*object)).value; }
70 
71  ValueType() = default;
72 
73  ValueType(const std::string& value) // NOLINT
74  { object = std::make_shared<ObjWrap<std::string>>(value); }
75 
76  ValueType(const LogCallback& cb) // NOLINT
77  : expectedKey("log_cb")
78  { object = std::make_shared<ObjWrap<LogCallback>>(cb); }
79 
80  ValueType(const ErrorCallback& cb) // NOLINT
81  : expectedKey("error_cb")
82  { object = std::make_shared<ObjWrap<ErrorCallback>>(cb); }
83 
84  ValueType(const StatsCallback& cb) // NOLINT
85  : expectedKey("stats_cb")
86  { object = std::make_shared<ObjWrap<StatsCallback>>(cb); }
87 
88  ValueType(const OauthbearerTokenRefreshCallback& cb) // NOLINT
89  : expectedKey("oauthbearer_token_refresh_cb")
90  { object = std::make_shared<ObjWrap<OauthbearerTokenRefreshCallback>>(cb); }
91 
92  ValueType(const Interceptors& interceptors) // NOLINT
93  : expectedKey("interceptors")
94  { object = std::make_shared<ObjWrap<Interceptors>>(interceptors); }
95 
96  bool operator==(const ValueType& rhs) const { return toString() == rhs.toString(); }
97 
98  std::string toString() const { return object->toString(); }
99 
100  private:
101  std::string expectedKey;
102  std::shared_ptr<Object> object;
103  };
104 
105 public:
106  // Just make sure key will printed in order
107  using PropertiesMap = std::map<std::string, ValueType>;
108 
109  Properties() = default;
110  Properties(const Properties&) = default;
111  Properties(PropertiesMap kvMap): _kvMap(std::move(kvMap)) // NOLINT
112  {
113  for (const auto& kv: _kvMap)
114  {
115  kv.second.validate(kv.first);
116  }
117  }
118  virtual ~Properties() = default;
119 
120  bool operator==(const Properties& rhs) const { return map() == rhs.map(); }
121 
126  template <class T>
127  Properties& put(const std::string& key, const T& value)
128  {
129  _kvMap[key] = ValueType(value).validate(key);
130  return *this;
131  }
132 
136  void remove(const std::string& key)
137  {
138  _kvMap.erase(key);
139  }
140 
144  bool contains(const std::string& key) const
145  {
146  auto search = _kvMap.find(key);
147  return search != _kvMap.end();
148  }
149 
154  template<class T>
155  T& get(const std::string& key) const
156  {
157  auto search = _kvMap.find(key);
158  if (search == _kvMap.end())
159  {
160  KAFKA_THROW_ERROR(Error(RD_KAFKA_RESP_ERR__INVALID_ARG, "Failed to get \"" + key + "\" from Properties!"));
161  }
162 
163  const ValueType& v = search->second;
164  return v.getValue<T>();
165  }
166 
170  Optional<std::string> getProperty(const std::string& key) const
171  {
172  if (!contains(key)) return Optional<std::string>{};
173 
174  try
175  {
176  return get<std::string>(key);
177  }
178  catch (const std::bad_cast&)
179  {
180  return Optional<std::string>{};
181  }
182  }
183 
187  void eraseProperty(const std::string& key)
188  {
189  _kvMap.erase(key);
190  }
191 
192  std::string toString() const
193  {
194 
195  std::string ret;
196  std::for_each(_kvMap.cbegin(), _kvMap.cend(),
197  [&ret](const auto& kv) {
198  const std::string& key = kv.first;
199  const std::string value = kv.second.toString();
200 
201  static const std::regex reSensitiveKey(R"(.+\.password|.+\.username|.+secret|.+key|.+pem)");
202  const bool isSensitive = std::regex_match(key, reSensitiveKey);
203 
204  ret.append(ret.empty() ? "" : "|").append(key).append("=").append(isSensitive ? "*" : value);
205  });
206  return ret;
207  }
208 
212  const PropertiesMap& map() const { return _kvMap; }
213 
214 private:
215  PropertiesMap _kvMap;
216 };
217 
218 } // end of KAFKA_API
219 
Unified error type.
Definition: Error.h:32
The properties for Kafka clients.
Definition: Properties.h:24
T & get(const std::string &key) const
Get a property reference.
Definition: Properties.h:155
void eraseProperty(const std::string &key)
Remove a property.
Definition: Properties.h:187
void remove(const std::string &key)
Remove the property (if one exists).
Definition: Properties.h:136
Properties & put(const std::string &key, const T &value)
Set a property.
Definition: Properties.h:127
bool contains(const std::string &key) const
Check whether the map contains a property.
Definition: Properties.h:144
const PropertiesMap & map() const
Get all properties with a map.
Definition: Properties.h:212
Optional< std::string > getProperty(const std::string &key) const
Get a property.
Definition: Properties.h:170
Interceptors for Kafka clients.
Definition: Interceptors.h:14