DYNCFG: health, generate userconfig for incomplete alerts (#17859)
* allow userconfig to generate incomplete configurations for health alerts * when adding an alert, add 1 rule by default
Costa Tsaousis committed
Jun 11, 2024 at 21:30 UTC
734272c21358f167a25781927193f399a79fa44e
3 files changed
+82
-79
src/health/health_dyncfg.c
+64
-62
@@ -53,112 +53,114 @@ static void data_source_to_rrdr_options(RRD_ALERT_PROTOTYPE *ap) {
53
}
54
}
55
56
-static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_match *match, BUFFER *error) {
56
+static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_match *match, BUFFER *error, bool strict) {
57
STRING *on = NULL;
58
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "on", on, error, true);
58
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "on", on, error, strict);
59
if(match->is_template)
60
match->on.context = on;
61
else
62
match->on.chart = on;
63
64
- JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error);
65
- JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error);
64
+ JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error, strict);
65
+ JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error, strict);
66
67
return true;
68
}
69
70
-static bool parse_config_value_database_lookup(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
71
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "after", config->after, error);
72
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "before", config->before, error);
73
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group", time_grouping_txt2id, config->time_group, error);
74
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "dims_group", alerts_dims_grouping2id, config->dims_group, error);
75
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "data_source", alerts_data_sources2id, config->data_source, error);
70
+static bool parse_config_value_database_lookup(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
71
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "after", config->after, error, strict);
72
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "before", config->before, error, strict);
73
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group", time_grouping_txt2id, config->time_group, error, strict);
74
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "dims_group", alerts_dims_grouping2id, config->dims_group, error, strict);
75
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "data_source", alerts_data_sources2id, config->data_source, error, strict);
76
77
switch(config->time_group) {
78
default:
79
break;
80
81
case RRDR_GROUPING_COUNTIF:
82
- JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group_condition", alerts_group_condition2id, config->time_group_condition, error);
82
+ JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group_condition", alerts_group_condition2id, config->time_group_condition, error, strict);
83
// fall through
84
85
case RRDR_GROUPING_TRIMMED_MEAN:
86
case RRDR_GROUPING_TRIMMED_MEDIAN:
87
case RRDR_GROUPING_PERCENTILE:
88
- JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "time_group_value", config->time_group_value, error);
88
+ JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "time_group_value", config->time_group_value, error, strict);
89
break;
90
}
91
92
- JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", rrdr_options_parse_one, config->options, error);
93
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "dimensions", config->dimensions, error, true);
92
+ JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", rrdr_options_parse_one, config->options, error, strict);
93
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "dimensions", config->dimensions, error, strict);
94
return true;
95
}
96
-static bool parse_config_value(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
97
- JSONC_PARSE_SUBOBJECT(jobj, path, "database_lookup", config, parse_config_value_database_lookup, error);
98
- JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "calculation", config->calculation, error);
99
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "units", config->units, error, true);
100
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "update_every", config->update_every, error);
96
+
97
+static bool parse_config_value(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
98
+ JSONC_PARSE_SUBOBJECT(jobj, path, "database_lookup", config, parse_config_value_database_lookup, error, strict);
99
+ JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "calculation", config->calculation, error, strict);
100
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "units", config->units, error, strict);
101
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "update_every", config->update_every, error, strict);
102
return true;
103
}
104
104
-static bool parse_config_conditions(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
105
- JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "warning_condition", config->warning, error);
106
- JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "critical_condition", config->critical, error);
105
+static bool parse_config_conditions(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
106
+ JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "warning_condition", config->warning, error, strict);
107
+ JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "critical_condition", config->critical, error, strict);
108
return true;
109
}
110
110
-static bool parse_config_action_delay(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
111
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "up", config->delay_up_duration, error);
112
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "down", config->delay_down_duration, error);
113
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "max", config->delay_max_duration, error);
114
- JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "multiplier", config->delay_multiplier, error);
111
+static bool parse_config_action_delay(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
112
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "up", config->delay_up_duration, error, strict);
113
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "down", config->delay_down_duration, error, strict);
114
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "max", config->delay_max_duration, error, strict);
115
+ JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "multiplier", config->delay_multiplier, error, strict);
116
return true;
117
}
117
-static bool parse_config_action_repeat(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
118
- JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "enabled", config->has_custom_repeat_config, error);
119
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "warning", config->warn_repeat_every, error);
120
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "critical", config->crit_repeat_every, error);
118
+
119
+static bool parse_config_action_repeat(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
120
+ JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "enabled", config->has_custom_repeat_config, error, strict);
121
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "warning", config->warn_repeat_every, error, strict);
122
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "critical", config->crit_repeat_every, error, strict);
123
return true;
124
}
125
124
-static bool parse_config_action(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
125
- JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", alert_action_options_parse_one, config->alert_action_options, error);
126
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "execute", config->exec, error, true);
127
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "recipient", config->recipient, error, true);
128
- JSONC_PARSE_SUBOBJECT(jobj, path, "delay", config, parse_config_action_delay, error);
129
- JSONC_PARSE_SUBOBJECT(jobj, path, "repeat", config, parse_config_action_repeat, error);
126
+static bool parse_config_action(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
127
+ JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", alert_action_options_parse_one, config->alert_action_options, error, strict);
128
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "execute", config->exec, error, strict);
129
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "recipient", config->recipient, error, strict);
130
+ JSONC_PARSE_SUBOBJECT(jobj, path, "delay", config, parse_config_action_delay, error, strict);
131
+ JSONC_PARSE_SUBOBJECT(jobj, path, "repeat", config, parse_config_action_repeat, error, strict);
132
return true;
133
}
134
133
-static bool parse_config(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *ap, BUFFER *error) {
135
+static bool parse_config(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *ap, BUFFER *error, bool strict) {
136
// we shouldn't parse these from the payload - they are given to us via the function call
135
- // JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "source_type", dyncfg_source_type2id, ap->config.source_type, error);
136
- // JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "source", ap->config.source, error, true);
137
+ // JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "source_type", dyncfg_source_type2id, ap->config.source_type, error, strict);
138
+ // JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "source", ap->config.source, error, strict);
139
138
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "summary", ap->config.summary, error, true);
139
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "info", ap->config.info, error, true);
140
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "type", ap->config.type, error, true);
141
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "component", ap->config.component, error, true);
142
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "classification", ap->config.classification, error, true);
140
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "summary", ap->config.summary, error, strict);
141
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "info", ap->config.info, error, strict);
142
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "type", ap->config.type, error, strict);
143
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "component", ap->config.component, error, strict);
144
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "classification", ap->config.classification, error, strict);
145
144
- JSONC_PARSE_SUBOBJECT(jobj, path, "value", &ap->config, parse_config_value, error);
145
- JSONC_PARSE_SUBOBJECT(jobj, path, "conditions", &ap->config, parse_config_conditions, error);
146
- JSONC_PARSE_SUBOBJECT(jobj, path, "action", &ap->config, parse_config_action, error);
147
- JSONC_PARSE_SUBOBJECT(jobj, path, "match", &ap->match, parse_match, error);
146
+ JSONC_PARSE_SUBOBJECT(jobj, path, "value", &ap->config, parse_config_value, error, strict);
147
+ JSONC_PARSE_SUBOBJECT(jobj, path, "conditions", &ap->config, parse_config_conditions, error, strict);
148
+ JSONC_PARSE_SUBOBJECT(jobj, path, "action", &ap->config, parse_config_action, error, strict);
149
+ JSONC_PARSE_SUBOBJECT(jobj, path, "match", &ap->match, parse_match, error, strict);
150
151
return true;
152
}
153
152
-static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *base, BUFFER *error, const char *name) {
154
+static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *base, BUFFER *error, const char *name, bool strict) {
155
int64_t version;
154
- JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "format_version", version, error);
156
+ JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "format_version", version, error, strict);
157
158
if(version != 1) {
159
buffer_sprintf(error, "unsupported document version");
160
return false;
161
}
162
161
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "name", base->config.name, error, !name && !*name);
163
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "name", base->config.name, error, !name && !*name && strict);
164
165
json_object *rules;
166
if (json_object_object_get_ex(jobj, "rules", &rules)) {
@@ -174,10 +176,10 @@ static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTO
176
177
json_object *rule = json_object_array_get_idx(rules, i);
178
177
- JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(rule, path, "enabled", ap->match.enabled, error);
179
+ JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(rule, path, "enabled", ap->match.enabled, error, strict);
180
181
STRING *type = NULL;
180
- JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(rule, path, "type", type, error, true);
182
+ JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(rule, path, "type", type, error, strict);
183
if(string_strcmp(type, "template") == 0)
184
ap->match.is_template = true;
185
else if(string_strcmp(type, "instance") == 0)
@@ -187,7 +189,7 @@ static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTO
189
return false;
190
}
191
190
- JSONC_PARSE_SUBOBJECT(rule, path, "config", ap, parse_config, error);
192
+ JSONC_PARSE_SUBOBJECT(rule, path, "config", ap, parse_config, error, strict);
193
194
ap = NULL; // so that we will create another one, if available
195
}
@@ -200,7 +202,7 @@ static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTO
202
return true;
203
}
204
203
-static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload, size_t payload_len, BUFFER *error, const char *name) {
205
+static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload, size_t payload_len, BUFFER *error, const char *name, bool strict) {
206
RRD_ALERT_PROTOTYPE *base = callocz(1, sizeof(*base));
207
CLEAN_JSON_OBJECT *jobj = NULL;
208
@@ -219,7 +221,7 @@ static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload,
221
}
222
json_tokener_free(tokener);
223
222
- if(!parse_prototype(jobj, "", base, error, name))
224
+ if(!parse_prototype(jobj, "", base, error, name, strict))
225
goto cleanup;
226
227
if(!base->config.name && name)
@@ -232,7 +234,7 @@ static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload,
234
ap->config.name = string_dup(base->config.name);
235
}
236
235
- if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation) {
237
+ if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation && strict) {
238
buffer_sprintf(error, "the rule No %d has neither database lookup nor calculation", i);
239
goto cleanup;
240
}
@@ -546,7 +548,7 @@ static int dyncfg_health_prototype_template_action(BUFFER *result, DYNCFG_CMDS c
548
switch(cmd) {
549
case DYNCFG_CMD_ADD: {
550
CLEAN_BUFFER *error = buffer_create(0, NULL);
549
- RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name);
551
+ RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name, true);
552
if(!nap)
553
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
554
else {
@@ -578,7 +580,7 @@ static int dyncfg_health_prototype_template_action(BUFFER *result, DYNCFG_CMDS c
580
581
case DYNCFG_CMD_USERCONFIG: {
582
CLEAN_BUFFER *error = buffer_create(0, NULL);
581
- RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name);
583
+ RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name, false);
584
if(!nap)
585
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
586
else {
@@ -671,7 +673,7 @@ static int dyncfg_health_prototype_job_action(BUFFER *result, DYNCFG_CMDS cmd, B
673
674
case DYNCFG_CMD_UPDATE: {
675
CLEAN_BUFFER *error = buffer_create(0, NULL);
674
- RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name);
676
+ RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name, true);
677
if(!nap)
678
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
679
else {
@@ -694,7 +696,7 @@ static int dyncfg_health_prototype_job_action(BUFFER *result, DYNCFG_CMDS cmd, B
696
697
case DYNCFG_CMD_USERCONFIG: {
698
CLEAN_BUFFER *error = buffer_create(0, NULL);
697
- RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name);
699
+ RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name, false);
700
if(!nap)
701
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
702
else {
src/health/schema.d/health%3Aalert%3Aprototype.json
+1
@@ -467,6 +467,7 @@
467
"ui:widget": "hidden"
468
},
469
"rules": {
470
+ "ui:openEmptyItem": true,
471
"items": {
472
"ui:classNames": "dyncfg-grid dyncfg-grid-col-6",
473
"enabled": {
src/libnetdata/json/json-c-parser-inline.h
+17
-17
@@ -3,11 +3,11 @@
3
#ifndef NETDATA_JSON_C_PARSER_INLINE_H
4
#define NETDATA_JSON_C_PARSER_INLINE_H
5
6
-#define JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
6
+#define JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
7
json_object *_j; \
8
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_boolean)) \
9
dst = json_object_get_boolean(_j); \
10
- else { \
10
+ else if(required) { \
11
buffer_sprintf(error, "missing or invalid type for '%s.%s' boolean", path, member); \
12
return false; \
13
} \
@@ -48,7 +48,7 @@
48
} \
49
} while(0)
50
51
-#define JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
51
+#define JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
52
json_object *_j; \
53
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
54
string_freez(dst); \
@@ -58,13 +58,13 @@
58
else \
59
dst = string_strdupz(_v); \
60
} \
61
- else { \
61
+ else if(required) { \
62
buffer_sprintf(error, "missing or invalid type for '%s.%s' string", path, member); \
63
return false; \
64
} \
65
} while(0)
66
67
-#define JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
67
+#define JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
68
json_object *_j; \
69
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
70
const char *_t = json_object_get_string(_j); \
@@ -80,13 +80,13 @@
80
} \
81
} \
82
} \
83
- else { \
83
+ else if(required) { \
84
buffer_sprintf(error, "missing or invalid type for '%s.%s' expression", path, member); \
85
return false; \
86
} \
87
} while(0)
88
89
-#define JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
89
+#define JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error, required) do { \
90
json_object *_jarray; \
91
if (json_object_object_get_ex(jobj, member, &_jarray) && json_object_is_type(_jarray, json_type_array)) { \
92
size_t _num_options = json_object_array_length(_jarray); \
@@ -105,24 +105,24 @@
105
} \
106
dst |= _bit; \
107
} \
108
- } else { \
108
+ } else if(required) { \
109
buffer_sprintf(error, "missing or invalid type for '%s.%s' array", path, member); \
110
return false; \
111
} \
112
} while(0)
113
114
115
-#define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
115
+#define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error, required) do { \
116
json_object *_j; \
117
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) \
118
dst = converter(json_object_get_string(_j)); \
119
- else { \
119
+ else if(required) { \
120
buffer_sprintf(error, "missing or invalid type (expected text value) for '%s.%s' enum", path, member); \
121
return false; \
122
} \
123
} while(0)
124
125
-#define JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
125
+#define JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
126
json_object *_j; \
127
if (json_object_object_get_ex(jobj, member, &_j)) { \
128
if (_j != NULL && json_object_is_type(_j, json_type_int)) \
@@ -135,13 +135,13 @@
135
buffer_sprintf(error, "not supported type (expected int) for '%s.%s'", path, member); \
136
return false; \
137
} \
138
- } else { \
138
+ } else if(required) { \
139
buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
140
return false; \
141
} \
142
} while(0)
143
144
-#define JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
144
+#define JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
145
json_object *_j; \
146
if (json_object_object_get_ex(jobj, member, &_j)) { \
147
if (_j != NULL && json_object_is_type(_j, json_type_double)) \
@@ -154,21 +154,21 @@
154
buffer_sprintf(error, "not supported type (expected double) for '%s.%s'", path, member); \
155
return false; \
156
} \
157
- } else { \
157
+ } else if(required) { \
158
buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
159
return false; \
160
} \
161
} while(0)
162
163
-#define JSONC_PARSE_SUBOBJECT(jobj, path, member, dst, callback, error) do { \
163
+#define JSONC_PARSE_SUBOBJECT(jobj, path, member, dst, callback, error, required) do { \
164
json_object *_j; \
165
if (json_object_object_get_ex(jobj, member, &_j)) { \
166
char _new_path[strlen(path) + strlen(member) + 2]; \
167
snprintfz(_new_path, sizeof(_new_path), "%s%s%s", path, *path?".":"", member); \
168
- if (!callback(_j, _new_path, dst, error)) { \
168
+ if (!callback(_j, _new_path, dst, error, required)) { \
169
return false; \
170
} \
171
- } else { \
171
+ } else if(required) { \
172
buffer_sprintf(error, "missing '%s.%s' object", path, member); \
173
return false; \
174
} \