@cryptotaxi247 / netdata-1 / commits / 734272c21

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 } \