Skip to content

Commit 257f5b3

Browse files
Issue: 4556521 Unified Configuration: missing fields
- Exsposed the config registry for use by other parts of the code - Refactored the display of non-default configurations to scan the config registry Signed-off-by: Oren Shemesh <[email protected]>
1 parent 5412ecf commit 257f5b3

File tree

16 files changed

+601
-417
lines changed

16 files changed

+601
-417
lines changed

src/core/config/config_registry.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ void config_registry::initialize_registry(std::queue<std::unique_ptr<loader>> &&
117117
key + "': expected " +
118118
get_user_friendly_type_name(param_desc.type()) + ", got " +
119119
get_user_friendly_type_name(value.type()));
120+
} catch (const std::runtime_error &e) {
121+
const parameter_descriptor param_desc = m_config_descriptor.get_parameter(key);
122+
throw_xlio_exception("In '" + loader->source() + "': " + e.what());
120123
}
121124
}
122125

src/core/config/config_registry.h

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,64 +69,41 @@ class config_registry {
6969
std::vector<std::string> get_sources() const;
7070

7171
/**
72-
* @brief Gets default value for non-integer types
72+
* @brief Gets default value
7373
* @tparam T Value type
7474
* @param key Configuration parameter key
7575
* @return Default value for the parameter
7676
*/
77-
template <typename T>
78-
typename std::enable_if<!is_integer<T>::value, T>::type get_default_value(
79-
const std::string &key) const
80-
{
81-
return get_value_impl<T>(
82-
key, [this](const std::string &k) { return get_default_value_as_any(k); });
83-
}
84-
85-
/**
86-
* @brief Gets default value for integer types with bounds checking
87-
* @tparam T Integer type
88-
* @param key Configuration parameter key
89-
* @return Default value for the parameter
90-
* @note For enums, use int instead since C++14 doesn't support bound checking for enums
91-
*/
92-
template <typename T>
93-
typename std::enable_if<is_integer<T>::value, T>::type get_default_value(
94-
const std::string &key) const
77+
template <typename T> T get_default_value(const std::string &key) const
9578
{
9679
return get_value_impl<T>(
9780
key, [this](const std::string &k) { return get_default_value_as_any(k); });
9881
}
9982

10083
/**
101-
* @brief Gets configured value for non-integer types
84+
* @brief Gets configured value. For integer types, also does bounds checking
10285
* @tparam T Value type
10386
* @param key Configuration parameter key
10487
* @return Current value for the parameter
10588
*/
106-
template <typename T>
107-
typename std::enable_if<!is_integer<T>::value, T>::type get_value(const std::string &key) const
89+
template <typename T> T get_value(const std::string &key) const
10890
{
10991
return get_value_impl<T>(key, [this](const std::string &k) { return get_value_as_any(k); });
11092
}
11193

11294
/**
113-
* @brief Gets configured value for integer types with bounds checking
114-
* @tparam T Integer type
95+
* @brief Gets configured value as any
11596
* @param key Configuration parameter key
116-
* @return Current value for the parameter
117-
* @note For enums, use int instead since C++14 doesn't support bound checking for enums
97+
* @return Current value for the parameter, as any
11898
*/
119-
template <typename T>
120-
typename std::enable_if<is_integer<T>::value, T>::type get_value(const std::string &key) const
121-
{
122-
return get_value_impl<T>(key, [this](const std::string &k) { return get_value_as_any(k); });
123-
}
99+
std::experimental::any get_value_as_any(const std::string &key) const;
100+
101+
const config_descriptor &get_config_descriptor() const { return m_config_descriptor; }
124102

125103
private:
126104
std::map<std::string, std::experimental::any> m_config_data;
127105
config_descriptor m_config_descriptor;
128106
std::vector<std::string> m_sources;
129-
std::experimental::any get_value_as_any(const std::string &key) const;
130107
std::experimental::any get_default_value_as_any(const std::string &key) const;
131108
void initialize_registry(std::queue<std::unique_ptr<loader>> &&value_loaders,
132109
std::unique_ptr<descriptor_provider> descriptor_provider);

src/core/config/descriptor_providers/json_descriptor_provider.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ std::unique_ptr<parameter_descriptor> json_descriptor_provider::create_descripto
152152
// Create parameter descriptor with default value
153153
auto descriptor = std::make_unique<parameter_descriptor>(*analysis.default_value);
154154

155+
descriptor->set_title(analysis.title);
156+
155157
// Apply constraints if present
156158
if (analysis.needs_constraint_validation()) {
157159
apply_constraints(descriptor.get(), analysis.constraint_cfg);

src/core/config/descriptor_providers/schema_analyzer.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <stdexcept>
1212
#include <functional>
1313
#include <algorithm>
14+
#include "vlogger/vlogger.h"
1415

1516
static void for_each_oneof_option(json_object *one_of_field,
1617
std::function<void(json_object *)> func)
@@ -161,6 +162,25 @@ std::experimental::optional<std::experimental::any> schema_analyzer::determine_d
161162
return json_utils::to_any_value(default_field);
162163
}
163164

165+
std::string schema_analyzer::determine_title()
166+
{
167+
json_object *title_field =
168+
json_utils::try_get_field(m_property_obj, config_strings::schema::JSON_TITLE);
169+
if (!title_field) {
170+
// Enforce title definition only for leafs and arrays. Objects are exempt.
171+
if (determine_value_type() != typeid(json_object *)) {
172+
throw_xlio_exception("Title must be a defined for: " + m_path + " - " +
173+
std::to_string(json_object_get_type(title_field)));
174+
}
175+
return "TITLE-NOT-SET";
176+
}
177+
if (json_object_get_type(title_field) != json_type_string) {
178+
throw_xlio_exception("Title must be a string for: " + m_path + " - " +
179+
std::to_string(json_object_get_type(title_field)));
180+
}
181+
return json_object_get_string(title_field);
182+
}
183+
164184
memory_size_extension_config_t schema_analyzer::analyze_memory_size_extension_config()
165185
{
166186
if (!has_memory_size_flag()) {
@@ -397,6 +417,7 @@ schema_analyzer::analysis_result::analysis_result(schema_analyzer &analyzer)
397417
: json_property_type(analyzer.determine_property_type())
398418
, value_type(analyzer.determine_value_type())
399419
, default_value(analyzer.determine_default_value(value_type))
420+
, title(analyzer.determine_title())
400421
, memory_cfg(analyzer.analyze_memory_size_extension_config())
401422
, constraint_cfg(analyzer.analyze_constraint_config())
402423
, enum_cfg(analyzer.analyze_enum_mapping_config())

src/core/config/descriptor_providers/schema_analyzer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class schema_analyzer {
7070
std::type_index value_type; /**< C++ type for the parameter value */
7171
std::experimental::optional<std::experimental::any>
7272
default_value; /**< Default value ready for use */
73+
std::string title; /**< Title of the parameter as defined in schema */
7374

7475
// Pre-parsed component configurations
7576
memory_size_extension_config_t memory_cfg; /**< Memory size transformation configuration */
@@ -120,6 +121,7 @@ class schema_analyzer {
120121
std::type_index determine_value_type();
121122
std::experimental::optional<std::experimental::any> determine_default_value(
122123
std::type_index type);
124+
std::string determine_title();
123125

124126
// Component configuration methods
125127
memory_size_extension_config_t analyze_memory_size_extension_config();

src/core/config/descriptors/config_descriptor.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ std::type_index config_descriptor::get_parent_expected_type(const std::string &k
8080
return typeid(std::map<std::string, std::experimental::any>);
8181
}
8282

83+
const config_descriptor::parameter_map_t &config_descriptor::get_parameter_map() const
84+
{
85+
return parameter_map;
86+
}
87+
8388
/**
8489
* @brief Calculates the Levenshtein distance between two strings.
8590
*

src/core/config/descriptors/config_descriptor.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
class config_descriptor {
2222
public:
23+
typedef std::map<std::string, parameter_descriptor> parameter_map_t;
24+
2325
/**
2426
* @brief Default constructor
2527
*/
@@ -55,11 +57,18 @@ class config_descriptor {
5557
*/
5658
std::type_index get_parent_expected_type(const std::string &key) const;
5759

60+
/**
61+
* @brief Gets the parameter map, allowing external users to iterate
62+
* over all parameters
63+
* @return The parameter map
64+
*/
65+
const parameter_map_t &get_parameter_map() const;
66+
5867
private:
5968
/**
6069
* @brief Map from parameter name to its descriptor
6170
*/
62-
std::map<std::string, parameter_descriptor> parameter_map;
71+
parameter_map_t parameter_map;
6372

6473
/**
6574
* @brief Set of all parameter keys for efficient prefix-based lookups

src/core/config/descriptors/parameter_descriptor.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
#include <climits>
1313
#include <sstream>
1414
#include <cctype>
15+
#include <numeric>
16+
17+
void parameter_descriptor::set_title(const std::string &title)
18+
{
19+
m_title = title;
20+
}
21+
22+
const std::string &parameter_descriptor::get_title() const
23+
{
24+
return m_title;
25+
}
1526

1627
/**
1728
* @brief Parse memory size string with suffixes (e.g., "4GB", "512MB", "1024KB", "1024B", "5G")
@@ -151,6 +162,7 @@ parameter_descriptor::parameter_descriptor(const parameter_descriptor &pd)
151162
, m_string_mapping(pd.m_string_mapping)
152163
, m_value_transformer(pd.m_value_transformer)
153164
, m_type(pd.m_type)
165+
, m_title(pd.m_title)
154166
{
155167
}
156168

@@ -196,7 +208,19 @@ std::experimental::any parameter_descriptor::convert_string_to_int64(const std::
196208
return it->second;
197209
}
198210

199-
throw std::experimental::bad_any_cast();
211+
// If no string mappings are defined, throw an exception which has no further information
212+
if (m_string_mapping.empty()) {
213+
throw std::experimental::bad_any_cast();
214+
}
215+
216+
// We have string mappings but value is not one of them - make an effort and create a nice error
217+
// message
218+
std::string valid_values = std::accumulate(
219+
next(m_string_mapping.begin()), m_string_mapping.end(), m_string_mapping.begin()->first,
220+
[](const std::string &a, const auto &b) { return a + "," + b.first; });
221+
222+
throw std::runtime_error("Invalid value for " + m_title + ": " + val + ", not one of: [" +
223+
valid_values + "]");
200224
}
201225

202226
std::experimental::any parameter_descriptor::get_value(bool val) const
@@ -226,6 +250,22 @@ std::experimental::any parameter_descriptor::get_value(const std::string &val) c
226250
throw std::experimental::bad_any_cast();
227251
}
228252

253+
std::string parameter_descriptor::convert_int64_to_mapped_string(int64_t val) const
254+
{
255+
if (m_string_mapping.empty()) {
256+
return std::string();
257+
}
258+
259+
for (const auto &mapping : m_string_mapping) {
260+
if ((mapping.second.type() == typeid(int64_t)) &&
261+
(std::experimental::any_cast<int64_t>(mapping.second) == val)) {
262+
return mapping.first;
263+
}
264+
}
265+
266+
return std::to_string(val) + " (Invalid value)";
267+
}
268+
229269
std::experimental::any parameter_descriptor::get_value(int64_t val) const
230270
{
231271
if (m_type != typeid(int64_t)) {

src/core/config/descriptors/parameter_descriptor.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ class parameter_descriptor {
8484
*/
8585
void set_value_transformer(value_transformer_t transformer);
8686

87+
/**
88+
* @brief Sets the title of the parameter
89+
* @param title Title of the parameter
90+
*/
91+
92+
void set_title(const std::string &title);
93+
/**
94+
* @brief Gets the title of the parameter
95+
* @return Title of the parameter
96+
*/
97+
const std::string &get_title() const;
98+
8799
/**
88100
* @brief Validates a value against all constraints
89101
* @param value Value to validate
@@ -138,6 +150,15 @@ class parameter_descriptor {
138150
*/
139151
std::experimental::any get_value(const std::vector<std::experimental::any> &val) const;
140152

153+
/**
154+
* @brief Converts an integer to a string per the string mappings set by set_string_mappings().
155+
* @param val Integer value to convert
156+
* @return Converted value. Empty string means no string mappings were set.
157+
If mappings were set, but the value is not in the mappings, returns the value as a
158+
string followed by "(Invalid value)".
159+
*/
160+
std::string convert_int64_to_mapped_string(int64_t val) const;
161+
141162
/**
142163
* @brief Gets the type of the parameter
143164
* @return The type of the parameter
@@ -162,6 +183,7 @@ class parameter_descriptor {
162183
std::map<std::string, std::experimental::any> m_string_mapping; /**< String-to-value mappings */
163184
value_transformer_t m_value_transformer; /**< Value transformation function */
164185
std::type_index m_type;
186+
std::string m_title; /**< Title of the parameter as defined in schema */
165187

166188
/**
167189
* @brief Parses a memory size string with suffixes (KB, MB, GB)

0 commit comments

Comments
 (0)