0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-17 11:12:42 +00:00

DYNCFG: health, generate userconfig for incomplete alerts ()

* allow userconfig to generate incomplete configurations for health alerts

* when adding an alert, add 1 rule by default
This commit is contained in:
Costa Tsaousis 2024-06-11 21:30:15 +03:00 committed by GitHub
parent 77358d9ee8
commit 734272c213
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 86 additions and 83 deletions

View file

@ -53,112 +53,114 @@ static void data_source_to_rrdr_options(RRD_ALERT_PROTOTYPE *ap) {
}
}
static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_match *match, BUFFER *error) {
static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_match *match, BUFFER *error, bool strict) {
STRING *on = NULL;
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "on", on, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "on", on, error, strict);
if(match->is_template)
match->on.context = on;
else
match->on.chart = on;
JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error);
JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error);
JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error, strict);
JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error, strict);
return true;
}
static bool parse_config_value_database_lookup(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "after", config->after, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "before", config->before, error);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group", time_grouping_txt2id, config->time_group, error);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "dims_group", alerts_dims_grouping2id, config->dims_group, error);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "data_source", alerts_data_sources2id, config->data_source, error);
static bool parse_config_value_database_lookup(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "after", config->after, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "before", config->before, error, strict);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group", time_grouping_txt2id, config->time_group, error, strict);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "dims_group", alerts_dims_grouping2id, config->dims_group, error, strict);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "data_source", alerts_data_sources2id, config->data_source, error, strict);
switch(config->time_group) {
default:
break;
case RRDR_GROUPING_COUNTIF:
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group_condition", alerts_group_condition2id, config->time_group_condition, error);
JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "time_group_condition", alerts_group_condition2id, config->time_group_condition, error, strict);
// fall through
case RRDR_GROUPING_TRIMMED_MEAN:
case RRDR_GROUPING_TRIMMED_MEDIAN:
case RRDR_GROUPING_PERCENTILE:
JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "time_group_value", config->time_group_value, error);
JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "time_group_value", config->time_group_value, error, strict);
break;
}
JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", rrdr_options_parse_one, config->options, error);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "dimensions", config->dimensions, error, true);
return true;
}
static bool parse_config_value(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_SUBOBJECT(jobj, path, "database_lookup", config, parse_config_value_database_lookup, error);
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "calculation", config->calculation, error);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "units", config->units, error, true);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "update_every", config->update_every, error);
JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", rrdr_options_parse_one, config->options, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "dimensions", config->dimensions, error, strict);
return true;
}
static bool parse_config_conditions(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "warning_condition", config->warning, error);
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "critical_condition", config->critical, error);
static bool parse_config_value(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_SUBOBJECT(jobj, path, "database_lookup", config, parse_config_value_database_lookup, error, strict);
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "calculation", config->calculation, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "units", config->units, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "update_every", config->update_every, error, strict);
return true;
}
static bool parse_config_action_delay(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "up", config->delay_up_duration, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "down", config->delay_down_duration, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "max", config->delay_max_duration, error);
JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "multiplier", config->delay_multiplier, error);
return true;
}
static bool parse_config_action_repeat(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "enabled", config->has_custom_repeat_config, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "warning", config->warn_repeat_every, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "critical", config->crit_repeat_every, error);
static bool parse_config_conditions(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "warning_condition", config->warning, error, strict);
JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "critical_condition", config->critical, error, strict);
return true;
}
static bool parse_config_action(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", alert_action_options_parse_one, config->alert_action_options, error);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "execute", config->exec, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "recipient", config->recipient, error, true);
JSONC_PARSE_SUBOBJECT(jobj, path, "delay", config, parse_config_action_delay, error);
JSONC_PARSE_SUBOBJECT(jobj, path, "repeat", config, parse_config_action_repeat, error);
static bool parse_config_action_delay(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "up", config->delay_up_duration, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "down", config->delay_down_duration, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "max", config->delay_max_duration, error, strict);
JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "multiplier", config->delay_multiplier, error, strict);
return true;
}
static bool parse_config(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *ap, BUFFER *error) {
static bool parse_config_action_repeat(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "enabled", config->has_custom_repeat_config, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "warning", config->warn_repeat_every, error, strict);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "critical", config->crit_repeat_every, error, strict);
return true;
}
static bool parse_config_action(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error, bool strict) {
JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", alert_action_options_parse_one, config->alert_action_options, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "execute", config->exec, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "recipient", config->recipient, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "delay", config, parse_config_action_delay, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "repeat", config, parse_config_action_repeat, error, strict);
return true;
}
static bool parse_config(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *ap, BUFFER *error, bool strict) {
// we shouldn't parse these from the payload - they are given to us via the function call
// JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "source_type", dyncfg_source_type2id, ap->config.source_type, error);
// JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "source", ap->config.source, error, true);
// JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "source_type", dyncfg_source_type2id, ap->config.source_type, error, strict);
// JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "source", ap->config.source, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "summary", ap->config.summary, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "info", ap->config.info, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "type", ap->config.type, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "component", ap->config.component, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "classification", ap->config.classification, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "summary", ap->config.summary, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "info", ap->config.info, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "type", ap->config.type, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "component", ap->config.component, error, strict);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "classification", ap->config.classification, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "value", &ap->config, parse_config_value, error);
JSONC_PARSE_SUBOBJECT(jobj, path, "conditions", &ap->config, parse_config_conditions, error);
JSONC_PARSE_SUBOBJECT(jobj, path, "action", &ap->config, parse_config_action, error);
JSONC_PARSE_SUBOBJECT(jobj, path, "match", &ap->match, parse_match, error);
JSONC_PARSE_SUBOBJECT(jobj, path, "value", &ap->config, parse_config_value, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "conditions", &ap->config, parse_config_conditions, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "action", &ap->config, parse_config_action, error, strict);
JSONC_PARSE_SUBOBJECT(jobj, path, "match", &ap->match, parse_match, error, strict);
return true;
}
static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *base, BUFFER *error, const char *name) {
static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *base, BUFFER *error, const char *name, bool strict) {
int64_t version;
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "format_version", version, error);
JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "format_version", version, error, strict);
if(version != 1) {
buffer_sprintf(error, "unsupported document version");
return false;
}
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "name", base->config.name, error, !name && !*name);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "name", base->config.name, error, !name && !*name && strict);
json_object *rules;
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
json_object *rule = json_object_array_get_idx(rules, i);
JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(rule, path, "enabled", ap->match.enabled, error);
JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(rule, path, "enabled", ap->match.enabled, error, strict);
STRING *type = NULL;
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(rule, path, "type", type, error, true);
JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(rule, path, "type", type, error, strict);
if(string_strcmp(type, "template") == 0)
ap->match.is_template = true;
else if(string_strcmp(type, "instance") == 0)
@ -187,7 +189,7 @@ static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTO
return false;
}
JSONC_PARSE_SUBOBJECT(rule, path, "config", ap, parse_config, error);
JSONC_PARSE_SUBOBJECT(rule, path, "config", ap, parse_config, error, strict);
ap = NULL; // so that we will create another one, if available
}
@ -200,7 +202,7 @@ static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTO
return true;
}
static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload, size_t payload_len, BUFFER *error, const char *name) {
static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload, size_t payload_len, BUFFER *error, const char *name, bool strict) {
RRD_ALERT_PROTOTYPE *base = callocz(1, sizeof(*base));
CLEAN_JSON_OBJECT *jobj = NULL;
@ -219,7 +221,7 @@ static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload,
}
json_tokener_free(tokener);
if(!parse_prototype(jobj, "", base, error, name))
if(!parse_prototype(jobj, "", base, error, name, strict))
goto cleanup;
if(!base->config.name && name)
@ -232,7 +234,7 @@ static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload,
ap->config.name = string_dup(base->config.name);
}
if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation) {
if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation && strict) {
buffer_sprintf(error, "the rule No %d has neither database lookup nor calculation", i);
goto cleanup;
}
@ -546,7 +548,7 @@ static int dyncfg_health_prototype_template_action(BUFFER *result, DYNCFG_CMDS c
switch(cmd) {
case DYNCFG_CMD_ADD: {
CLEAN_BUFFER *error = buffer_create(0, NULL);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name, true);
if(!nap)
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
else {
@ -578,7 +580,7 @@ static int dyncfg_health_prototype_template_action(BUFFER *result, DYNCFG_CMDS c
case DYNCFG_CMD_USERCONFIG: {
CLEAN_BUFFER *error = buffer_create(0, NULL);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name, false);
if(!nap)
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
else {
@ -671,7 +673,7 @@ static int dyncfg_health_prototype_job_action(BUFFER *result, DYNCFG_CMDS cmd, B
case DYNCFG_CMD_UPDATE: {
CLEAN_BUFFER *error = buffer_create(0, NULL);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name, true);
if(!nap)
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
else {
@ -694,7 +696,7 @@ static int dyncfg_health_prototype_job_action(BUFFER *result, DYNCFG_CMDS cmd, B
case DYNCFG_CMD_USERCONFIG: {
CLEAN_BUFFER *error = buffer_create(0, NULL);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name);
RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name, false);
if(!nap)
code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
else {

View file

@ -467,6 +467,7 @@
"ui:widget": "hidden"
},
"rules": {
"ui:openEmptyItem": true,
"items": {
"ui:classNames": "dyncfg-grid dyncfg-grid-col-6",
"enabled": {

View file

@ -3,11 +3,11 @@
#ifndef NETDATA_JSON_C_PARSER_INLINE_H
#define NETDATA_JSON_C_PARSER_INLINE_H
#define JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
#define JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_boolean)) \
dst = json_object_get_boolean(_j); \
else { \
else if(required) { \
buffer_sprintf(error, "missing or invalid type for '%s.%s' boolean", path, member); \
return false; \
} \
@ -48,7 +48,7 @@
} \
} while(0)
#define JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
#define JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
string_freez(dst); \
@ -58,13 +58,13 @@
else \
dst = string_strdupz(_v); \
} \
else { \
else if(required) { \
buffer_sprintf(error, "missing or invalid type for '%s.%s' string", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
#define JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
const char *_t = json_object_get_string(_j); \
@ -80,13 +80,13 @@
} \
} \
} \
else { \
else if(required) { \
buffer_sprintf(error, "missing or invalid type for '%s.%s' expression", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
#define JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error, required) do { \
json_object *_jarray; \
if (json_object_object_get_ex(jobj, member, &_jarray) && json_object_is_type(_jarray, json_type_array)) { \
size_t _num_options = json_object_array_length(_jarray); \
@ -105,24 +105,24 @@
} \
dst |= _bit; \
} \
} else { \
} else if(required) { \
buffer_sprintf(error, "missing or invalid type for '%s.%s' array", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
#define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) \
dst = converter(json_object_get_string(_j)); \
else { \
else if(required) { \
buffer_sprintf(error, "missing or invalid type (expected text value) for '%s.%s' enum", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
#define JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j)) { \
if (_j != NULL && json_object_is_type(_j, json_type_int)) \
@ -135,13 +135,13 @@
buffer_sprintf(error, "not supported type (expected int) for '%s.%s'", path, member); \
return false; \
} \
} else { \
} else if(required) { \
buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
#define JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j)) { \
if (_j != NULL && json_object_is_type(_j, json_type_double)) \
@ -154,21 +154,21 @@
buffer_sprintf(error, "not supported type (expected double) for '%s.%s'", path, member); \
return false; \
} \
} else { \
} else if(required) { \
buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
return false; \
} \
} while(0)
#define JSONC_PARSE_SUBOBJECT(jobj, path, member, dst, callback, error) do { \
#define JSONC_PARSE_SUBOBJECT(jobj, path, member, dst, callback, error, required) do { \
json_object *_j; \
if (json_object_object_get_ex(jobj, member, &_j)) { \
char _new_path[strlen(path) + strlen(member) + 2]; \
snprintfz(_new_path, sizeof(_new_path), "%s%s%s", path, *path?".":"", member); \
if (!callback(_j, _new_path, dst, error)) { \
if (!callback(_j, _new_path, dst, error, required)) { \
return false; \
} \
} else { \
} else if(required) { \
buffer_sprintf(error, "missing '%s.%s' object", path, member); \
return false; \
} \