diff options
52 files changed, 5298 insertions, 770 deletions
diff --git a/.gitignore b/.gitignore index d5d0e6131a8..0b0f6883213 100644 --- a/.gitignore +++ b/.gitignore @@ -126,10 +126,11 @@ tests/libs/zbxdbcache/zbx_vc_get_value tests/libs/zbxdbcache/zbx_vc_get_values tests/libs/zbxdbhigh/DBselect_uint64 tests/libs/zbxhistory/zbx_history_get_values -tests/libs/zbxjson/zbx_json_path_open -tests/libs/zbxjson/jsonpath_next +tests/libs/zbxjson/zbx_json_open_path tests/libs/zbxjson/zbx_json_decodevalue tests/libs/zbxjson/zbx_json_decodevalue_dyn +tests/libs/zbxjson/zbx_jsonpath_compile +tests/libs/zbxjson/zbx_jsonpath_query tests/libs/zbxprometheus/prometheus_filter_init tests/libs/zbxprometheus/prometheus_parse_row tests/libs/zbxprometheus/zbx_prometheus_pattern @@ -146,6 +147,7 @@ tests/libs/zbxsysinfo/linux/NET_IF_TOTAL tests/libs/zbxsysinfo/linux/SYSTEM_BOOTTIME tests/libs/zbxsysinfo/linux/SYSTEM_CPU_INTR tests/libs/zbxsysinfo/linux/SYSTEM_CPU_SWITCHES +tests/libs/zbxsysinfo/linux/SYSTEM_HW_CHASSIS tests/libs/zbxsysinfo/linux/VFS_FS_DISCOVERY tests/libs/zbxsysinfo/linux/SYSTEM_HW_CHASSIS tests/libs/zbxsysinfo/parse_item_key diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent index 88f3f3f0f32..230f67177bc 100644 --- a/build/win32/project/Makefile_agent +++ b/build/win32/project/Makefile_agent @@ -41,6 +41,7 @@ OBJS = \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ ..\..\..\src\libs\zbxcommon\misc.o \ + ..\..\..\src\libs\zbxcommon\variant.o \ ..\..\..\src\libs\zbxcommon\str.o \ ..\..\..\src\libs\zbxcommon\xml.o \ ..\..\..\src\libs\zbxcommon\zbxgetopt.o \ @@ -53,6 +54,7 @@ OBJS = \ ..\..\..\src\libs\zbxcrypto\md5.o \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ + ..\..\..\src\libs\zbxjson\jsonpath.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxsys\mutexs.o \ ..\..\..\src\libs\zbxsys\symbols.o \ diff --git a/build/win32/project/Makefile_sender b/build/win32/project/Makefile_sender index acf754df55b..1d07cea9796 100644 --- a/build/win32/project/Makefile_sender +++ b/build/win32/project/Makefile_sender @@ -31,6 +31,7 @@ OBJS = \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ ..\..\..\src\libs\zbxcommon\misc.o \ + ..\..\..\src\libs\zbxcommon\variant.o \ ..\..\..\src\libs\zbxcommon\str.o \ ..\..\..\src\libs\zbxcommon\xml.o \ ..\..\..\src\libs\zbxcommon\zbxgetopt.o \ @@ -41,11 +42,15 @@ OBJS = \ ..\..\..\src\libs\zbxcrypto\md5.o \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ + ..\..\..\src\libs\zbxjson\jsonpath.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxsys\mutexs.o \ ..\..\..\src\libs\zbxsys\symbols.o \ ..\..\..\src\libs\zbxsys\threads.o \ ..\..\..\src\libs\zbxwin32\fatal.o \ + ..\..\..\src\libs\zbxalgo\algodefs.o \ + ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\zabbix_sender\zabbix_sender.o LIBS = ws2_32.lib psapi.lib pdh.lib Wldap32.lib advapi32.lib uuid.lib Iphlpapi.lib user32.lib diff --git a/build/win32/project/Makefile_sender_dll b/build/win32/project/Makefile_sender_dll index 271ffc7a7f6..6cbb0b6697f 100644 --- a/build/win32/project/Makefile_sender_dll +++ b/build/win32/project/Makefile_sender_dll @@ -37,6 +37,7 @@ OBJS = \ ..\..\..\src\libs\zbxcommon\comms.o \ ..\..\..\src\libs\zbxcommon\iprange.o \ ..\..\..\src\libs\zbxcommon\misc.o \ + ..\..\..\src\libs\zbxcommon\variant.o \ ..\..\..\src\libs\zbxcommon\str.o \ ..\..\..\src\libs\zbxcommon\xml.o \ ..\..\..\src\libs\zbxcommon\zbxgetopt.o \ @@ -47,11 +48,15 @@ OBJS = \ ..\..\..\src\libs\zbxcrypto\md5.o \ ..\..\..\src\libs\zbxjson\json.o \ ..\..\..\src\libs\zbxjson\json_parser.o \ + ..\..\..\src\libs\zbxjson\jsonpath.o \ ..\..\..\src\libs\zbxlog\log.o \ ..\..\..\src\libs\zbxsys\mutexs.o \ ..\..\..\src\libs\zbxsys\symbols.o \ ..\..\..\src\libs\zbxsys\threads.o \ ..\..\..\src\libs\zbxwin32\fatal.o \ + ..\..\..\src\libs\zbxalgo\algodefs.o \ + ..\..\..\src\libs\zbxalgo\vector.o \ + ..\..\..\src\libs\zbxregexp\zbxregexp.o \ ..\..\..\src\zabbix_sender\win32\zabbix_sender.o LIBS = ws2_32.lib psapi.lib pdh.lib Wldap32.lib advapi32.lib uuid.lib Iphlpapi.lib diff --git a/configure.ac b/configure.ac index a9258a770e6..01cc034c8f8 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,7 @@ AC_CHECK_HEADERS(stdio.h stdlib.h string.h unistd.h netdb.h signal.h \ stdarg.h winsock2.h pdh.h psapi.h sys/sem.h sys/ipc.h sys/shm.h Winldap.h \ Winber.h lber.h ws2tcpip.h inttypes.h sys/file.h grp.h \ execinfo.h sys/systemcfg.h sys/mnttab.h mntent.h sys/times.h \ - dlfcn.h sys/utsname.h sys/un.h sys/protosw.h) + dlfcn.h sys/utsname.h sys/un.h sys/protosw.h stddef.h) AC_CHECK_HEADERS(resolv.h, [], [], [ #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> diff --git a/include/common.h b/include/common.h index dcc2ce58c1a..a46b54210bc 100644 --- a/include/common.h +++ b/include/common.h @@ -1485,18 +1485,16 @@ int zbx_strmatch_condition(const char *value, const char *pattern, unsigned char zbx_log_value_t *zbx_log_value_dup(const zbx_log_value_t *src); -typedef void * zbx_variant_data_bin_t; - typedef union { - zbx_uint64_t ui64; - double dbl; + zbx_uint64_t ui64; + double dbl; /* null terminated string */ - char *str; + char *str; /* length prefixed (4 bytes) binary data */ - zbx_variant_data_bin_t *bin; + void *bin; } zbx_variant_data_t; @@ -1518,8 +1516,8 @@ void zbx_variant_set_none(zbx_variant_t *value); void zbx_variant_set_str(zbx_variant_t *value, char *text); void zbx_variant_set_dbl(zbx_variant_t *value, double dbl); void zbx_variant_set_ui64(zbx_variant_t *value, zbx_uint64_t ui64); -void zbx_variant_set_bin(zbx_variant_t *value, zbx_variant_data_bin_t *value_bin); -void zbx_variant_set_variant(zbx_variant_t *value, const zbx_variant_t *source); +void zbx_variant_set_bin(zbx_variant_t *value, void *value_bin); +void zbx_variant_copy(zbx_variant_t *value, const zbx_variant_t *source); int zbx_variant_set_numeric(zbx_variant_t *value, const char *text); int zbx_variant_convert(zbx_variant_t *value, int type); @@ -1527,12 +1525,13 @@ const char *zbx_get_variant_type_desc(unsigned char type); const char *zbx_variant_value_desc(const zbx_variant_t *value); const char *zbx_variant_type_desc(const zbx_variant_t *value); -int zbx_validate_value_dbl(double value); int zbx_variant_compare(const zbx_variant_t *value1, const zbx_variant_t *value2); -zbx_variant_data_bin_t *zbx_variant_data_bin_copy(const zbx_variant_data_bin_t *bin); -zbx_variant_data_bin_t *zbx_variant_data_bin_create(const void *data, zbx_uint32_t size); -zbx_uint32_t zbx_variant_data_bin_get(const zbx_variant_data_bin_t *bin, void **data); +void *zbx_variant_data_bin_copy(const void *bin); +void *zbx_variant_data_bin_create(const void *data, zbx_uint32_t size); +zbx_uint32_t zbx_variant_data_bin_get(const void *bin, void **data); + +int zbx_validate_value_dbl(double value); void zbx_update_env(double time_now); diff --git a/include/dbcache.h b/include/dbcache.h index 76217bcddc4..98ab8e08372 100644 --- a/include/dbcache.h +++ b/include/dbcache.h @@ -938,7 +938,7 @@ zbx_lld_macro_path_t; int zbx_lld_macro_paths_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *lld_macro_paths, char **error); void zbx_lld_macro_path_free(zbx_lld_macro_path_t *lld_macro_path); int zbx_lld_macro_value_by_name(const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, - const char *macro, char **value, size_t *value_alloc); + const char *macro, char **value); int zbx_lld_macro_paths_compare(const void *d1, const void *d2); void zbx_dc_get_item_tags_by_functionids(const zbx_uint64_t *functionids, size_t functionids_num, zbx_vector_ptr_t *host_tags); diff --git a/include/sysinc.h b/include/sysinc.h index fcf5ccf6f4b..a0beb02c0a3 100644 --- a/include/sysinc.h +++ b/include/sysinc.h @@ -420,4 +420,8 @@ # include <utmpx.h> #endif +#ifdef HAVE_STDDEF_H +# include <stddef.h> +#endif + #endif diff --git a/include/zbxjson.h b/include/zbxjson.h index 7b37f98b5ae..d220f23b8fa 100644 --- a/include/zbxjson.h +++ b/include/zbxjson.h @@ -220,8 +220,23 @@ const char *zbx_json_decodevalue(const char *p, char *string, size_t size, zbx_j const char *zbx_json_decodevalue_dyn(const char *p, char **string, size_t *string_alloc, zbx_json_type_t *type); void zbx_json_escape(char **string); -int zbx_json_path_check(const char *path, char * error, size_t errlen); -int zbx_json_path_open(const struct zbx_json_parse *jp, const char *path, struct zbx_json_parse *out); -void zbx_json_value_dyn(const struct zbx_json_parse *jp, char **string, size_t *string_alloc); +/* jsonpath support */ + +typedef struct zbx_jsonpath_segment zbx_jsonpath_segment_t; + +typedef struct +{ + zbx_jsonpath_segment_t *segments; + int segments_num; + int segments_alloc; + + /* set to 1 when jsonpath points at single location */ + unsigned char definite; +} +zbx_jsonpath_t; + +void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath); +int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath); +int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output); #endif /* ZABBIX_ZJSON_H */ diff --git a/include/zbxserver.h b/include/zbxserver.h index c07cf2b0ea7..00874380ae4 100644 --- a/include/zbxserver.h +++ b/include/zbxserver.h @@ -77,8 +77,6 @@ void zbx_determine_items_in_expressions(zbx_vector_ptr_t *trigger_order, const z #define ZBX_MACRO_SIMPLE (ZBX_MACRO_ANY | ZBX_TOKEN_SIMPLE_MACRO) #define ZBX_MACRO_FUNC (ZBX_MACRO_ANY | ZBX_TOKEN_FUNC_MACRO) -int zbx_lld_macro_value_by_name(const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, - const char *macro, char **value, size_t *value_alloc); int substitute_lld_macros(char **data, const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, int flags, char *error, size_t max_error_len); int substitute_key_macros(char **data, zbx_uint64_t *hostid, DC_ITEM *dc_item, const struct zbx_json_parse *jp_row, diff --git a/src/libs/zbxalgo/hashset.c b/src/libs/zbxalgo/hashset.c index 7eb5813b2b1..92c43ed78e8 100644 --- a/src/libs/zbxalgo/hashset.c +++ b/src/libs/zbxalgo/hashset.c @@ -17,8 +17,6 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ -#include <stddef.h> - #include "common.h" #include "log.h" diff --git a/src/libs/zbxcommon/variant.c b/src/libs/zbxcommon/variant.c index 27230dd5edb..d7382e252b3 100644 --- a/src/libs/zbxcommon/variant.c +++ b/src/libs/zbxcommon/variant.c @@ -20,10 +20,10 @@ #include "common.h" #include "zbxalgo.h" -zbx_variant_data_bin_t *zbx_variant_data_bin_copy(const zbx_variant_data_bin_t *bin) +void *zbx_variant_data_bin_copy(const void *bin) { zbx_uint32_t size; - zbx_variant_data_bin_t *value_bin; + void *value_bin; memcpy(&size, bin, sizeof(size)); value_bin = zbx_malloc(NULL, size + sizeof(size)); @@ -32,9 +32,9 @@ zbx_variant_data_bin_t *zbx_variant_data_bin_copy(const zbx_variant_data_bin_t * return value_bin; } -zbx_variant_data_bin_t *zbx_variant_data_bin_create(const void *data, zbx_uint32_t size) +void *zbx_variant_data_bin_create(const void *data, zbx_uint32_t size) { - zbx_variant_data_bin_t *value_bin; + void *value_bin; value_bin = zbx_malloc(NULL, size + sizeof(size)); memcpy(value_bin, &size, sizeof(size)); @@ -43,13 +43,13 @@ zbx_variant_data_bin_t *zbx_variant_data_bin_create(const void *data, zbx_uint32 return value_bin; } -zbx_uint32_t zbx_variant_data_bin_get(const zbx_variant_data_bin_t *bin, void **data) +zbx_uint32_t zbx_variant_data_bin_get(const void *bin, void **data) { zbx_uint32_t size; memcpy(&size, bin, sizeof(zbx_uint32_t)); if (NULL != data) - *data = ((char *)bin) + sizeof(size); + *data = ((unsigned char *)bin) + sizeof(size); return size; } @@ -68,6 +68,18 @@ void zbx_variant_clear(zbx_variant_t *value) value->type = ZBX_VARIANT_NONE; } +/****************************************************************************** + * * + * Setter functions assign passed data and set corresponding variant * + * type. Note that for complex data it means the pointer is simply copied * + * instead of making a copy of the specified data. * + * * + * The contents of the destination value are not freed. When setting already * + * initialized variant it's safer to clear it beforehand, even if the variant * + * contains primitive value (numeric). * + * * + ******************************************************************************/ + void zbx_variant_set_str(zbx_variant_t *value, char *text) { value->data.str = text; @@ -91,13 +103,26 @@ void zbx_variant_set_none(zbx_variant_t *value) value->type = ZBX_VARIANT_NONE; } -void zbx_variant_set_bin(zbx_variant_t *value, zbx_variant_data_bin_t *value_bin) +void zbx_variant_set_bin(zbx_variant_t *value, void *value_bin) { value->data.bin = value_bin; value->type = ZBX_VARIANT_BIN; } -void zbx_variant_set_variant(zbx_variant_t *value, const zbx_variant_t *source) +/****************************************************************************** + * * + * Function: zbx_variant_copy * + * * + * Purpose: copy variant contents from source to value * + * * + * Comments: String and binary data are cloned, which is different from * + * setters where only the pointers are copied. * + * The contents of the destination value are not freed. If copied * + * over already initialized variant it's safer to clear it * + * beforehand. * + * * + ******************************************************************************/ +void zbx_variant_copy(zbx_variant_t *value, const zbx_variant_t *source) { switch (source->type) { @@ -264,9 +289,8 @@ int zbx_variant_set_numeric(zbx_variant_t *value, const char *text) const char *zbx_variant_value_desc(const zbx_variant_t *value) { - static char buffer[ZBX_MAX_UINT64_LEN + 1]; - int i, len; - zbx_uint32_t size; + ZBX_THREAD_LOCAL static char buffer[ZBX_MAX_UINT64_LEN + 1]; + zbx_uint32_t size, i, len; switch (value->type) { @@ -342,7 +366,8 @@ int zbx_validate_value_dbl(double value) * * * Function: variant_compare_empty * * * - * Purpose: compares two variant values when at least one is empty * + * Purpose: compares two variant values when at least one is empty (having * + * type of ZBX_VARIANT_NONE) * * * ******************************************************************************/ static int variant_compare_empty(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -362,7 +387,7 @@ static int variant_compare_empty(const zbx_variant_t *value1, const zbx_variant_ * * * Function: variant_compare_bin * * * - * Purpose: compares two variant values when at least one contains binary data* + * Purpose: compare two variant values when at least one contains binary data * * * ******************************************************************************/ static int variant_compare_bin(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -387,7 +412,7 @@ static int variant_compare_bin(const zbx_variant_t *value1, const zbx_variant_t * * * Function: variant_compare_str * * * - * Purpose: compares two variant values when at least one is string * + * Purpose: compare two variant values when at least one is string * * * ******************************************************************************/ static int variant_compare_str(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -402,8 +427,9 @@ static int variant_compare_str(const zbx_variant_t *value1, const zbx_variant_t * * * Function: variant_compare_dbl * * * - * Purpose: compares two variant values when at least one is double and the * - * other is double or uint64 * + * Purpose: compare two variant values when at least one is double and the * + * other is double, uint64 or a string representing a valid double * + * value * * * ******************************************************************************/ static int variant_compare_dbl(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -418,6 +444,9 @@ static int variant_compare_dbl(const zbx_variant_t *value1, const zbx_variant_t case ZBX_VARIANT_UI64: value1_dbl = value1->data.ui64; break; + case ZBX_VARIANT_STR: + value1_dbl = atof(value1->data.str); + break; default: THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); @@ -431,6 +460,9 @@ static int variant_compare_dbl(const zbx_variant_t *value1, const zbx_variant_t case ZBX_VARIANT_UI64: value2_dbl = value2->data.ui64; break; + case ZBX_VARIANT_STR: + value2_dbl = atof(value2->data.str); + break; default: THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); @@ -440,6 +472,7 @@ static int variant_compare_dbl(const zbx_variant_t *value1, const zbx_variant_t return 0; ZBX_RETURN_IF_NOT_EQUAL(value1_dbl, value2_dbl); + THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); } @@ -448,7 +481,7 @@ static int variant_compare_dbl(const zbx_variant_t *value1, const zbx_variant_t * * * Function: variant_compare_ui64 * * * - * Purpose: compares two variant values when both are uint64 * + * Purpose: compare two variant values when both are uint64 * * * ******************************************************************************/ static int variant_compare_ui64(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -461,25 +494,25 @@ static int variant_compare_ui64(const zbx_variant_t *value1, const zbx_variant_t * * * Function: zbx_variant_compare * * * - * Purpose: compares two variant values * + * Purpose: compare two variant values * * * * Parameters: value1 - [IN] the first value * * value2 - [IN] the second value * * * - * Return value: <0 - the first value is less than second * - * >0 - the first value is greater than second * + * Return value: <0 - the first value is less than the second * + * >0 - the first value is greater than the second * * 0 - the values are equal * * * - * Comments: The following priority is applied: * - * 1) value of none type is always less than other types, two * - * none types are equal * - * 1) value of binary type is always greater than other types, two * + * Comments: The following comparison logic is applied: * + * 1) value of 'none' type is always less than other types, two * + * 'none' types are equal * + * 2) value of binary type is always greater than other types, two * * binary types are compared by length and then by contents * - * 2) if any of value is of string type, the other is converted to * + * 3) if both values have uint64 types, they are compared as is * + * 4) if both values can be converted to floating point values the * + * conversion is done and the result is compared * + * 5) if any of value is of string type, the other is converted to * * string and both are compared * - * 3) if any of value is of floating type, the other is converted * - * to floating value and both are compared * - * 4) only uin64 types are left, compare as uin64 * * * ******************************************************************************/ int zbx_variant_compare(const zbx_variant_t *value1, const zbx_variant_t *value2) @@ -490,17 +523,15 @@ int zbx_variant_compare(const zbx_variant_t *value1, const zbx_variant_t *value2 if (ZBX_VARIANT_BIN == value1->type || ZBX_VARIANT_BIN == value2->type) return variant_compare_bin(value1, value2); - if (ZBX_VARIANT_STR == value1->type || ZBX_VARIANT_STR == value2->type) - return variant_compare_str(value1, value2); + if (ZBX_VARIANT_UI64 == value1->type && ZBX_VARIANT_UI64 == value2->type) + return variant_compare_ui64(value1, value2); - if (ZBX_VARIANT_DBL == value1->type || ZBX_VARIANT_DBL == value2->type) + if ((ZBX_VARIANT_STR != value1->type || SUCCEED == is_double(value1->data.str)) && + (ZBX_VARIANT_STR != value2->type || SUCCEED == is_double(value2->data.str))) + { return variant_compare_dbl(value1, value2); + } - if (ZBX_VARIANT_UI64 == value1->type && ZBX_VARIANT_UI64 == value2->type) - return variant_compare_ui64(value1, value2); - - THIS_SHOULD_NEVER_HAPPEN; - exit(EXIT_FAILURE); + /* at this point at least one of the values is string data, other can be uint64, floating or string */ + return variant_compare_str(value1, value2); } - - diff --git a/src/libs/zbxdbhigh/lld_macro.c b/src/libs/zbxdbhigh/lld_macro.c index 4c7445346e2..3ee2e7644ba 100644 --- a/src/libs/zbxdbhigh/lld_macro.c +++ b/src/libs/zbxdbhigh/lld_macro.c @@ -54,7 +54,6 @@ int zbx_lld_macro_paths_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *lld_macro DB_ROW row; zbx_lld_macro_path_t *lld_macro_path; int ret = SUCCEED; - char err[MAX_STRING_LEN]; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); @@ -67,14 +66,18 @@ int zbx_lld_macro_paths_get(zbx_uint64_t lld_ruleid, zbx_vector_ptr_t *lld_macro while (NULL != (row = DBfetch(result))) { - if (SUCCEED != (ret = zbx_json_path_check(row[1], err, sizeof(err)))) + zbx_jsonpath_t path; + + if (SUCCEED != (ret = zbx_jsonpath_compile(row[1], &path))) { - *error = zbx_dsprintf(*error, "Cannot process LLD macro \"%s\": %s.\n", row[0], err); + *error = zbx_dsprintf(*error, "Cannot process LLD macro \"%s\": %s.\n", row[0], + zbx_json_strerror()); break; } - lld_macro_path = (zbx_lld_macro_path_t *)zbx_malloc(NULL, sizeof(zbx_lld_macro_path_t)); + zbx_jsonpath_clear(&path); + lld_macro_path = (zbx_lld_macro_path_t *)zbx_malloc(NULL, sizeof(zbx_lld_macro_path_t)); lld_macro_path->lld_macro = zbx_strdup(NULL, row[0]); lld_macro_path->path = zbx_strdup(NULL, row[1]); @@ -114,16 +117,14 @@ void zbx_lld_macro_path_free(zbx_lld_macro_path_t *lld_macro_path) * lld_macro_paths - [IN] use json path to extract from jp_row * * macro - [IN] LLD macro * * value - [OUT] value extracted from jp_row * - * value_alloc - [OUT] allocated memory size for value * * * ******************************************************************************/ int zbx_lld_macro_value_by_name(const struct zbx_json_parse *jp_row, const zbx_vector_ptr_t *lld_macro_paths, - const char *macro, char **value, size_t *value_alloc) + const char *macro, char **value) { zbx_lld_macro_path_t lld_macro_path_local, *lld_macro_path; int index; - struct zbx_json_parse jp_out; - int ret; + size_t value_alloc = 0; lld_macro_path_local.lld_macro = (char *)macro; @@ -132,12 +133,12 @@ int zbx_lld_macro_value_by_name(const struct zbx_json_parse *jp_row, const zbx_v { lld_macro_path = (zbx_lld_macro_path_t *)lld_macro_paths->values[index]; - if (FAIL != (ret = zbx_json_path_open(jp_row, lld_macro_path->path, &jp_out))) - zbx_json_value_dyn(&jp_out, value, value_alloc); + if (SUCCEED == zbx_jsonpath_query(jp_row, lld_macro_path->path, value) && NULL != *value) + return SUCCEED; + + return FAIL; } else - ret = zbx_json_value_by_name_dyn(jp_row, macro, value, value_alloc); - - return ret; + return zbx_json_value_by_name_dyn(jp_row, macro, value, &value_alloc); } diff --git a/src/libs/zbxjson/Makefile.am b/src/libs/zbxjson/Makefile.am index facf5959937..721ac2e79d3 100644 --- a/src/libs/zbxjson/Makefile.am +++ b/src/libs/zbxjson/Makefile.am @@ -3,6 +3,6 @@ noinst_LIBRARIES = libzbxjson.a libzbxjson_a_SOURCES = \ - json.c \ - json_parser.c \ - json_parser.h + json.c json.h \ + json_parser.c json_parser.h \ + jsonpath.c jsonpath.h diff --git a/src/libs/zbxjson/json.c b/src/libs/zbxjson/json.c index 21541f2dd39..d3785b0964d 100644 --- a/src/libs/zbxjson/json.c +++ b/src/libs/zbxjson/json.c @@ -20,6 +20,8 @@ #include "common.h" #include "zbxjson.h" #include "json_parser.h" +#include "json.h" +#include "jsonpath.h" /****************************************************************************** * * @@ -34,15 +36,14 @@ ******************************************************************************/ #define ZBX_JSON_MAX_STRERROR 255 -static char zbx_json_strerror_message[ZBX_JSON_MAX_STRERROR]; +ZBX_THREAD_LOCAL static char zbx_json_strerror_message[ZBX_JSON_MAX_STRERROR]; const char *zbx_json_strerror(void) { return zbx_json_strerror_message; } -__zbx_attr_format_printf(1, 2) -static void zbx_set_json_strerror(const char *fmt, ...) +void zbx_set_json_strerror(const char *fmt, ...) { va_list args; @@ -1212,186 +1213,71 @@ int zbx_json_count(const struct zbx_json_parse *jp) return num; } - -/* - * limited JSONPath support - */ - -#define ZBX_JSONPATH_COMPONENT_DOT 0 -#define ZBX_JSONPATH_COMPONENT_BRACKET 1 -#define ZBX_JSONPATH_ARRAY_INDEX 2 - -/****************************************************************************** - * * - * Function: zbx_jsonpath_error * - * * - * Purpose: sets json error message and returns FAIL * - * * - * Comments: This function is used to return from json path parsing functions * - * in the case of failure. * - * * - ******************************************************************************/ -static int zbx_jsonpath_error(const char *path) -{ - zbx_set_json_strerror("unsupported character in json path starting with: \"%s\"", path); - return FAIL; -} - /****************************************************************************** * * - * Function: jsonpath_next * + * Function: zbx_json_open_path * * * - * Purpose: returns next component of json path * + * Purpose: opens an object by definite json path * * * - * Parameters: path - [IN] the json path * - * pnext - [IN/OUT] the reference to the next path component * - * loc - [OUT] the location of the path component * - * type - [OUT] json path component type, see ZBX_JSONPATH_ * - * defines * + * Return value: SUCCESS - processed successfully * + * FAIL - an error occurred * * * - * Return value: SUCCEED - the json path component was parsed successfully * - * FAIL - json path parsing error * + * Comments: Only direct path to single object in dot or bracket notation * + * is supported. * * * ******************************************************************************/ -static int jsonpath_next(const char *path, const char **pnext, zbx_strloc_t *loc, int *type) +int zbx_json_open_path(const struct zbx_json_parse *jp, const char *path, struct zbx_json_parse *out) { - const char *next = *pnext; - size_t pos; - char quotes; + int i, ret = FAIL; + struct zbx_json_parse object; + zbx_jsonpath_t jsonpath; - if (NULL == next) - { - if ('$' != *path) - return zbx_jsonpath_error(path); + object = *jp; - next = path + 1; - *pnext = next; - } + if (FAIL == zbx_jsonpath_compile(path, &jsonpath)) + return FAIL; - /* process dot notation component */ - if (*next == '.') + if (0 == jsonpath.definite) { - if ('\0' == *(++next)) - return zbx_jsonpath_error(*pnext); - - loc->l = next - path; - - while (0 != isalnum(*next) || '_' == *next) - next++; - - if ((pos = next - path) == loc->l) - return zbx_jsonpath_error(*pnext); - - loc->r = pos - 1; - *pnext = next; - *type = ZBX_JSONPATH_COMPONENT_DOT; - - return SUCCEED; + zbx_set_json_strerror("cannot use indefinite path when opening sub element"); + goto out; } - if ('[' != *next) - return zbx_jsonpath_error(*pnext); - - SKIP_WHITESPACE_NEXT(next); - - /* process array index component */ - if (0 != isdigit(*next)) + for (i = 0; i < jsonpath.segments_num; i++) { - for (pos = 1; 0 != isdigit(next[pos]); pos++) - ; + const char *p; + zbx_jsonpath_segment_t *segment = &jsonpath.segments[i]; - loc->l = next - path; - loc->r = loc->l + pos - 1; - - next += pos; - *type = ZBX_JSONPATH_ARRAY_INDEX; - - SKIP_WHITESPACE(next); - } - else - { - loc->l = next - path + 1; - - for (quotes = *next++; quotes != *next; next++) + if (ZBX_JSONPATH_SEGMENT_MATCH_LIST != segment->type) { - if ('\0' == *next) - return zbx_jsonpath_error(*pnext); + zbx_set_json_strerror("jsonpath segment %d is not a name or index", i + 1); + goto out; } - if ((pos = next - path) == loc->l) - return zbx_jsonpath_error(*pnext); - - loc->r = pos - 1; - *type = ZBX_JSONPATH_COMPONENT_BRACKET; - - SKIP_WHITESPACE_NEXT(next); - } - - if (']' != *next++) - return zbx_jsonpath_error(*pnext); - - *pnext = next; - - return SUCCEED; -} - -/****************************************************************************** - * * - * Function: zbx_json_path_open * - * * - * Purpose: opens an object by json path * - * * - * Return value: SUCCESS - processed successfully * - * FAIL - an error occurred * - * * - * Comments: Only direct path to single object in dot or bracket notation * - * is supported. * - * * - ******************************************************************************/ -int zbx_json_path_open(const struct zbx_json_parse *jp, const char *path, struct zbx_json_parse *out) -{ - const char *p, *next = 0; - char buffer[MAX_STRING_LEN]; - zbx_strloc_t loc; - int type, index; - struct zbx_json_parse object; - - object = *jp; - - do - { - if (FAIL == jsonpath_next(path, &next, &loc, &type)) - return FAIL; - - if (ZBX_JSONPATH_ARRAY_INDEX == type) + if (ZBX_JSONPATH_LIST_INDEX == segment->data.list.type) { + int index; + if ('[' != *object.start) - return FAIL; + goto out; - if (FAIL == is_uint_n_range(path + loc.l, loc.r - loc.l + 1, &index, sizeof(index), 0, - 0xFFFFFFFF)) - { - return FAIL; - } + memcpy(&index, segment->data.list.values->data, sizeof(int)); for (p = NULL; NULL != (p = zbx_json_next(&object, p)) && 0 != index; index--) ; if (0 != index || NULL == p) { - zbx_set_json_strerror("array index out of bounds starting with json path: \"%s\"", - path + loc.l); - return FAIL; + zbx_set_json_strerror("array index out of bounds in jsonpath segment %d", i + 1); + goto out; } } else { - zbx_strlcpy(buffer, path + loc.l, loc.r - loc.l + 2); - - if (NULL == (p = zbx_json_pair_by_name(&object, buffer))) + if (NULL == (p = zbx_json_pair_by_name(&object, (char *)&segment->data.list.values->data))) { - zbx_set_json_strerror("object not found starting with json path: \"%s\"", path + loc.l); - return FAIL; + zbx_set_json_strerror("object not found in jsonpath segment %d", i + 1); + goto out; } } @@ -1400,65 +1286,10 @@ int zbx_json_path_open(const struct zbx_json_parse *jp, const char *path, struct if (NULL == (object.end = __zbx_json_rbracket(p))) object.end = p + json_parse_value(p, NULL) - 1; } - while ('\0' != *next); *out = object; - - return SUCCEED; -} - -/****************************************************************************** - * * - * Function: zbx_json_value_dyn * - * * - * Purpose: return json fragment or value located at json parse location * - * * - ******************************************************************************/ -void zbx_json_value_dyn(const struct zbx_json_parse *jp, char **string, size_t *string_alloc) -{ - if (NULL == zbx_json_decodevalue_dyn(jp->start, string, string_alloc, NULL)) - { - size_t len = jp->end - jp->start + 2; - - if (*string_alloc < len) - *string = (char *)zbx_realloc(*string, len); - - zbx_strlcpy(*string, jp->start, len); - } -} - -/****************************************************************************** - * * - * Function: zbx_json_path_check * - * * - * Purpose: validate json path string * - * * - * Parameters: path - [IN] the json path * - * error - [OUT] the error message buffer * - * errlen - [IN] the size of error message buffer * - * * - * Return value: SUCCEED - the json path component was parsed successfully * - * FAIL - json path parsing error * - * * - ******************************************************************************/ -int zbx_json_path_check(const char *path, char * error, size_t errlen) -{ - const char *next = NULL; - zbx_strloc_t loc; - int type; - - do - { - if (SUCCEED != jsonpath_next(path, &next, &loc, &type)) - { - zbx_snprintf(error, errlen, "json path not valid: %s", zbx_json_strerror()); - return FAIL; - } - } - while ('\0' != *next); - - return SUCCEED; + ret = SUCCEED; +out: + zbx_jsonpath_clear(&jsonpath); + return ret; } -#ifdef HAVE_TESTS -# include "../../../tests/libs/zbxjson/jsonpath_next_test.c" -#endif diff --git a/tests/libs/zbxjson/jsonpath_next_test.h b/src/libs/zbxjson/json.h index cd824446165..81e02e2ee0b 100644 --- a/tests/libs/zbxjson/jsonpath_next_test.h +++ b/src/libs/zbxjson/json.h @@ -17,9 +17,18 @@ ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ -#ifndef JSONPATH_NEXT_TEST_H -#define JSONPATH_NEXT_TEST_H +#ifndef ZABBIX_JSON_H +#define ZABBIX_JSON_H -int zbx_jsonpath_next(const char *path, const char **pnext, zbx_strloc_t *loc, int *type); +#define SKIP_WHITESPACE(src) \ + while ('\0' != *(src) && NULL != strchr(ZBX_WHITESPACE, *(src))) (src)++ + +/* can only be used on non empty string */ +#define SKIP_WHITESPACE_NEXT(src)\ + (src)++; \ + SKIP_WHITESPACE(src) + +void zbx_set_json_strerror(const char *fmt, ...) __zbx_attr_format_printf(1, 2); +int zbx_json_open_path(const struct zbx_json_parse *jp, const char *path, struct zbx_json_parse *out); #endif diff --git a/src/libs/zbxjson/json_parser.c b/src/libs/zbxjson/json_parser.c index f04a28d6c69..05c4cd88dc4 100644 --- a/src/libs/zbxjson/json_parser.c +++ b/src/libs/zbxjson/json_parser.c @@ -21,6 +21,7 @@ #include "zbxjson.h" #include "json_parser.h" +#include "json.h" #include "log.h" diff --git a/src/libs/zbxjson/json_parser.h b/src/libs/zbxjson/json_parser.h index 2d7d28d7030..379d2b44258 100644 --- a/src/libs/zbxjson/json_parser.h +++ b/src/libs/zbxjson/json_parser.h @@ -20,13 +20,6 @@ #ifndef ZABBIX_JSON_PARSER_H #define ZABBIX_JSON_PARSER_H -#define SKIP_WHITESPACE(src) \ - while ('\0' != *(src) && NULL != strchr(ZBX_WHITESPACE, *(src))) (src)++ - -#define SKIP_WHITESPACE_NEXT(src)\ - (src)++; \ - SKIP_WHITESPACE(src) - int zbx_json_validate(const char *start, char **error); int json_parse_value(const char *start, char **error); diff --git a/src/libs/zbxjson/jsonpath.c b/src/libs/zbxjson/jsonpath.c new file mode 100644 index 00000000000..57003f95410 --- /dev/null +++ b/src/libs/zbxjson/jsonpath.c @@ -0,0 +1,2504 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "common.h" +#include "log.h" +#include "zbxalgo.h" +#include "zbxregexp.h" +#include "zbxjson.h" +#include "json.h" +#include "json_parser.h" +#include "jsonpath.h" + +#include "../zbxalgo/vectorimpl.h" + +ZBX_VECTOR_DECL(var, zbx_variant_t) +ZBX_VECTOR_IMPL(var, zbx_variant_t) + +static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects); +static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects); + +typedef struct +{ + zbx_jsonpath_token_group_t group; + int precedence; +} +zbx_jsonpath_token_def_t; + +/* define token groups and precedence */ +static zbx_jsonpath_token_def_t jsonpath_tokens[] = { + {0, 0}, + {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_PATH_RELATIVE */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_CONST_STR */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERAND, 0}, /* ZBX_JSONPATH_TOKEN_CONST_NUM */ + {ZBX_JSONPATH_TOKEN_GROUP_NONE, 0}, /* ZBX_JSONPATH_TOKEN_PAREN_LEFT */ + {ZBX_JSONPATH_TOKEN_GROUP_NONE, 0}, /* ZBX_JSONPATH_TOKEN_PAREN_RIGHT */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 4}, /* ZBX_JSONPATH_TOKEN_OP_PLUS */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 4}, /* ZBX_JSONPATH_TOKEN_OP_MINUS */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 3}, /* ZBX_JSONPATH_TOKEN_OP_MULT */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 3}, /* ZBX_JSONPATH_TOKEN_OP_DIV */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7}, /* ZBX_JSONPATH_TOKEN_OP_EQ */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7}, /* ZBX_JSONPATH_TOKEN_OP_NE */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_GT */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_GE */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_LT */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 6}, /* ZBX_JSONPATH_TOKEN_OP_LE */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR1, 2}, /* ZBX_JSONPATH_TOKEN_OP_NOT */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 11}, /* ZBX_JSONPATH_TOKEN_OP_AND */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 12}, /* ZBX_JSONPATH_TOKEN_OP_OR */ + {ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, 7} /* ZBX_JSONPATH_TOKEN_OP_REGEXP */ +}; + +static int jsonpath_token_precedence(int type) +{ + return jsonpath_tokens[type].precedence; +} + +static int jsonpath_token_group(int type) +{ + return jsonpath_tokens[type].group; +} + +/****************************************************************************** + * * + * Function: zbx_jsonpath_error * + * * + * Purpose: set json error message and return FAIL * + * * + * Comments: This function is used to return from json path parsing functions * + * in the case of failure. * + * * + ******************************************************************************/ +static int zbx_jsonpath_error(const char *path) +{ + if ('\0' != *path) + zbx_set_json_strerror("unsupported construct in jsonpath starting with: \"%s\"", path); + else + zbx_set_json_strerror("jsonpath was unexpectedly terminated"); + + return FAIL; +} + +/****************************************************************************** + * * + * Function: jsonpath_strndup * + * * + ******************************************************************************/ +static char *jsonpath_strndup(const char *source, size_t len) +{ + char *str; + + str = (char *)zbx_malloc(NULL, len + 1); + memcpy(str, source, len); + str[len] = '\0'; + + return str; +} + +/****************************************************************************** + * * + * Function: jsonpath_unquote * + * * + * Purpose: unquote single or double quoted string by stripping * + * leading/trailing quotes and unescaping backslash sequences * + * * + * Parameters: value - [OUT] the output value, must have at least len bytes * + * start - [IN] a single or double quoted string to unquote * + * len - [IN] the length of the input string * + * * + ******************************************************************************/ +static void jsonpath_unquote(char *value, const char *start, size_t len) +{ + const char *end = start + len - 1; + + for (start++; start != end; start++) + { + if ('\\' == *start) + start++; + + *value++ = *start; + } + + *value = '\0'; +} + +/****************************************************************************** + * * + * Function: jsonpath_unquote_dyn * + * * + * Purpose: unquote string stripping leading/trailing quotes and unescaping * + * backspace sequences * + * * + * Parameters: start - [IN] the string to unquote including leading and * + * trailing quotes * + * len - [IN] the length of the input string * + * * + * Return value: The unescaped string (must be freed by the caller). * + * * + ******************************************************************************/ +static char *jsonpath_unquote_dyn(const char *start, size_t len) +{ + char *value; + + value = (char *)zbx_malloc(NULL, len + 1); + jsonpath_unquote(value, start, len); + + return value; +} + +/****************************************************************************** + * * + * Function: jsonpath_list_create_item * + * * + * Purpose: create jsonpath list item of the specified size * + * * + ******************************************************************************/ +static zbx_jsonpath_list_node_t *jsonpath_list_create_node(size_t size) +{ + return (zbx_jsonpath_list_node_t *)zbx_malloc(NULL, offsetof(zbx_jsonpath_list_node_t, data) + size); +} + +/****************************************************************************** + * * + * Function: jsonpath_list_free * + * * + * Purpose: free jsonpath list * + * * + ******************************************************************************/ +static void jsonpath_list_free(zbx_jsonpath_list_node_t *list) +{ + zbx_jsonpath_list_node_t *item = list; + + while (NULL != list) + { + item = list; + list = list->next; + zbx_free(item); + } +} + +/****************************************************************************** + * * + * Function: jsonpath_create_token * + * * + * Purpose: create jsonpath expression token * + * * + * Parameters: type - [IN] the token type * + * expression - [IN] the expression * + * loc - [IN] the token location in the expression * + * * + * Return value: The created token (must be freed by the caller). * + * * + ******************************************************************************/ +static zbx_jsonpath_token_t *jsonpath_create_token(int type, const char *expression, const zbx_strloc_t *loc) +{ + zbx_jsonpath_token_t *token; + + token = (zbx_jsonpath_token_t *)zbx_malloc(NULL, sizeof(zbx_jsonpath_token_t)); + token->type = type; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_CONST_STR: + token->data = jsonpath_unquote_dyn(expression + loc->l, loc->r - loc->l + 1); + break; + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + case ZBX_JSONPATH_TOKEN_CONST_NUM: + token->data = jsonpath_strndup(expression + loc->l, loc->r - loc->l + 1); + break; + default: + token->data = NULL; + } + + return token; +} + +/****************************************************************************** + * * + * Function: jsonpath_token_free * + * * + ******************************************************************************/ +static void jsonpath_token_free(zbx_jsonpath_token_t *token) +{ + zbx_free(token->data); + zbx_free(token); +} + +/****************************************************************************** + * * + * Function: jsonpath_reserve * + * * + * Purpose: reserve space in jsonpath segments array for more segments * + * * + * Parameters: jsonpath - [IN] the jsonpath data * + * num - [IN] the number of segments to reserve * + * * + ******************************************************************************/ +static void jsonpath_reserve(zbx_jsonpath_t *jsonpath, int num) +{ + if (jsonpath->segments_num + num > jsonpath->segments_alloc) + { + int old_alloc = jsonpath->segments_alloc; + + if (jsonpath->segments_alloc < num) + jsonpath->segments_alloc = jsonpath->segments_num + num; + else + jsonpath->segments_alloc *= 2; + + jsonpath->segments = (zbx_jsonpath_segment_t *)zbx_realloc(jsonpath->segments, + sizeof(zbx_jsonpath_segment_t) * jsonpath->segments_alloc); + + /* Initialize the memory allocated for new segments, as parser can set */ + /* detached flag for the next segment, so the memory cannot be initialized */ + /* when creating a segment. */ + memset(jsonpath->segments + old_alloc, 0, + (jsonpath->segments_alloc - old_alloc) * sizeof(zbx_jsonpath_segment_t)); + } +} + +/****************************************************************************** + * * + * Function: jsonpath_segment_clear * + * * + ******************************************************************************/ +static void jsonpath_segment_clear(zbx_jsonpath_segment_t *segment) +{ + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + jsonpath_list_free(segment->data.list.values); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + zbx_vector_ptr_clear_ext(&segment->data.expression.tokens, + (zbx_clean_func_t)jsonpath_token_free); + zbx_vector_ptr_destroy(&segment->data.expression.tokens); + break; + default: + break; + } +} + +/****************************************************************************** + * * + * Function: jsonpath_next * + * * + * Purpose: find next component of json path * + * * + * Parameters: pnext - [IN/OUT] the reference to the next path component * + * * + * Return value: SUCCEED - the json path component was parsed successfully * + * FAIL - json path parsing error * + * * + ******************************************************************************/ +static int jsonpath_next(const char **pnext) +{ + const char *next = *pnext, *start; + + /* process dot notation component */ + if ('.' == *next) + { + if ('\0' == *(++next)) + return zbx_jsonpath_error(*pnext); + + if ('[' != *next) + { + start = next; + + while (0 != isalnum((unsigned char)*next) || '_' == *next) + next++; + + if (start == next) + return zbx_jsonpath_error(*pnext); + + *pnext = next; + return SUCCEED; + } + } + + if ('[' != *next) + return zbx_jsonpath_error(*pnext); + + SKIP_WHITESPACE_NEXT(next); + + /* process array index component */ + if (0 != isdigit((unsigned char)*next)) + { + size_t pos; + + for (pos = 1; 0 != isdigit((unsigned char)next[pos]); pos++) + ; + + next += pos; + SKIP_WHITESPACE(next); + } + else + { + char quotes; + + if ('\'' != *next && '"' != *next) + return zbx_jsonpath_error(*pnext); + + start = next; + + for (quotes = *next++; quotes != *next; next++) + { + if ('\0' == *next) + return zbx_jsonpath_error(*pnext); + } + + if (start == next) + return zbx_jsonpath_error(*pnext); + + SKIP_WHITESPACE_NEXT(next); + } + + if (']' != *next++) + return zbx_jsonpath_error(*pnext); + + *pnext = next; + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_substring * + * * + * Purpose: parse single or double quoted substring * + * * + * Parameters: start - [IN] the substring start * + * len - [OUT] the substring length * + * * + * Return value: SUCCEED - the substring was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_parse_substring(const char *start, int *len) +{ + const char *ptr; + char quotes; + + for (quotes = *start, ptr = start + 1; '\0' != *ptr; ptr++) + { + if (*ptr == quotes) + { + *len = ptr - start + 1; + return SUCCEED; + } + + if ('\\' == *ptr) + { + if (quotes != ptr[1] && '\\' != ptr[1] ) + return FAIL; + ptr++; + } + } + + return FAIL; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_path * + * * + * Purpose: parse jsonpath reference * + * * + * Parameters: start - [IN] the jsonpath start * + * len - [OUT] the jsonpath length * + * * + * Return value: SUCCEED - the jsonpath was parsed successfully * + * FAIL - otherwise * + * * + * Comments: This function is used to parse jsonpath references used in * + * jsonpath filter expressions. * + * * + ******************************************************************************/ +static int jsonpath_parse_path(const char *start, int *len) +{ + const char *ptr = start + 1; + + while ('[' == *ptr || '.' == *ptr) + { + if (FAIL == jsonpath_next(&ptr)) + return FAIL; + } + + *len = ptr - start; + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_number * + * * + * Purpose: parse number value * + * * + * Parameters: start - [IN] the number start * + * len - [OUT] the number length * + * * + * Return value: SUCCEED - the number was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_parse_number(const char *start, int *len) +{ + const char *ptr = start; + int size; + + if ('-' == *ptr || '+' == *ptr) + ptr++; + + if (FAIL == zbx_number_parse(ptr, &size)) + return FAIL; + + ptr += size; + + if ('e' == *ptr || 'E' == *ptr) + { + ptr++; + + if ('-' == *ptr || '+' == *ptr) + ptr++; + + if (0 == isdigit((unsigned char)*ptr)) + return FAIL; + + while (0 != isdigit((unsigned char)*ptr)) + ptr++; + } + + *len = ptr - start; + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_expression_next_token * + * * + * Purpose: get next token in jsonpath expression * + * * + * Parameters: exprsesion - [IN] the jsonpath expression * + * pos - [IN] the position of token in the expression * + * prev_group - [IN] the preceding token group, used to determine * + * token type based on context if necessary * + * type - [OUT] the token type * + * loc - [OUT] the token location in the expression * + * * + * Return value: SUCCEED - the token was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_expression_next_token(const char *expression, int pos, int prev_group, + zbx_jsonpath_token_type_t *type, zbx_strloc_t *loc) +{ + int len; + const char *ptr = expression + pos; + + SKIP_WHITESPACE(ptr); + loc->l = ptr - expression; + + switch (*ptr) + { + case '(': + *type = ZBX_JSONPATH_TOKEN_PAREN_LEFT; + loc->r = loc->l; + return SUCCEED; + case ')': + *type = ZBX_JSONPATH_TOKEN_PAREN_RIGHT; + loc->r = loc->l; + return SUCCEED; + case '+': + *type = ZBX_JSONPATH_TOKEN_OP_PLUS; + loc->r = loc->l; + return SUCCEED; + case '-': + if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group) + { + *type = ZBX_JSONPATH_TOKEN_OP_MINUS; + loc->r = loc->l; + return SUCCEED; + } + break; + case '/': + *type = ZBX_JSONPATH_TOKEN_OP_DIV; + loc->r = loc->l; + return SUCCEED; + case '*': + *type = ZBX_JSONPATH_TOKEN_OP_MULT; + loc->r = loc->l; + return SUCCEED; + case '!': + if ('=' == ptr[1]) + { + *type = ZBX_JSONPATH_TOKEN_OP_NE; + loc->r = loc->l + 1; + return SUCCEED; + } + *type = ZBX_JSONPATH_TOKEN_OP_NOT; + loc->r = loc->l; + return SUCCEED; + case '=': + switch (ptr[1]) + { + case '=': + *type = ZBX_JSONPATH_TOKEN_OP_EQ; + loc->r = loc->l + 1; + return SUCCEED; + case '~': + *type = ZBX_JSONPATH_TOKEN_OP_REGEXP; + loc->r = loc->l + 1; + return SUCCEED; + } + goto out; + case '<': + if ('=' == ptr[1]) + { + *type = ZBX_JSONPATH_TOKEN_OP_LE; + loc->r = loc->l + 1; + return SUCCEED; + } + *type = ZBX_JSONPATH_TOKEN_OP_LT; + loc->r = loc->l; + return SUCCEED; + case '>': + if ('=' == ptr[1]) + { + *type = ZBX_JSONPATH_TOKEN_OP_GE; + loc->r = loc->l + 1; + return SUCCEED; + } + *type = ZBX_JSONPATH_TOKEN_OP_GT; + loc->r = loc->l; + return SUCCEED; + case '|': + if ('|' == ptr[1]) + { + *type = ZBX_JSONPATH_TOKEN_OP_OR; + loc->r = loc->l + 1; + return SUCCEED; + } + goto out; + case '&': + if ('&' == ptr[1]) + { + *type = ZBX_JSONPATH_TOKEN_OP_AND; + loc->r = loc->l + 1; + return SUCCEED; + } + goto out; + case '@': + if (SUCCEED == jsonpath_parse_path(ptr, &len)) + { + *type = ZBX_JSONPATH_TOKEN_PATH_RELATIVE; + loc->r = loc->l + len - 1; + return SUCCEED; + } + goto out; + + case '$': + if (SUCCEED == jsonpath_parse_path(ptr, &len)) + { + *type = ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE; + loc->r = loc->l + len - 1; + return SUCCEED; + } + goto out; + case '\'': + case '"': + if (SUCCEED == jsonpath_parse_substring(ptr, &len)) + { + *type = ZBX_JSONPATH_TOKEN_CONST_STR; + loc->r = loc->l + len - 1; + return SUCCEED; + } + goto out; + } + + if ('-' == *ptr || 0 != isdigit((unsigned char)*ptr)) + { + if (SUCCEED == jsonpath_parse_number(ptr, &len)) + { + *type = ZBX_JSONPATH_TOKEN_CONST_NUM; + loc->r = loc->l + len - 1; + return SUCCEED; + } + } +out: + return zbx_jsonpath_error(ptr); +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_expression * + * * + * Purpose: parse jsonpath filter expression in format * + * * + * Parameters: expression - [IN] the expression, including opening and * + * closing parenthesis * + * jsonpath - [IN/OUT] the jsonpath * + * next - [OUT] a pointer to the next character after * + * parsed expression * + * * + * Return value: SUCCEED - the expression was parsed successfully * + * FAIL - otherwise * + * * + * Comments: This function uses shunting-yard algorithm to store parsed * + * tokens in postfix notation for evaluation. * + * * + * The following token precedence rules are enforced: * + * 1) binary operator must follow an operand * + * 2) operand must follow an operator * + * 3) unary operator must follow an operator * + * 4) ')' must follow an operand * + * * + ******************************************************************************/ +static int jsonpath_parse_expression(const char *expression, zbx_jsonpath_t *jsonpath, const char **next) +{ + int nesting = 1, ret = FAIL; + zbx_jsonpath_token_t *optoken; + zbx_vector_ptr_t output, operators; + zbx_strloc_t loc = {0, 0}; + zbx_jsonpath_token_type_t token_type; + zbx_jsonpath_token_group_t prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE; + + if ('(' != *expression) + return zbx_jsonpath_error(expression); + + zbx_vector_ptr_create(&output); + zbx_vector_ptr_create(&operators); + + while (SUCCEED == jsonpath_expression_next_token(expression, loc.r + 1, prev_group, &token_type, &loc)) + { + switch (token_type) + { + case ZBX_JSONPATH_TOKEN_PAREN_LEFT: + nesting++; + break; + + case ZBX_JSONPATH_TOKEN_PAREN_RIGHT: + if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group) + { + zbx_jsonpath_error(expression + loc.l); + goto out; + } + + if (0 == --nesting) + { + *next = expression + loc.r + 1; + ret = SUCCEED; + goto out; + } + break; + default: + break; + } + + if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == jsonpath_token_group(token_type)) + { + /* expression cannot have two consequent operands */ + if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group) + { + zbx_jsonpath_error(expression + loc.l); + goto out; + } + + zbx_vector_ptr_append(&output, jsonpath_create_token(token_type, expression, &loc)); + prev_group = jsonpath_token_group(token_type); + continue; + } + + if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token_type) || + ZBX_JSONPATH_TOKEN_GROUP_OPERATOR1 == jsonpath_token_group(token_type)) + { + /* binary operator must follow an operand */ + if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token_type) && + ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group) + { + zbx_jsonpath_error(expression + loc.l); + goto cleanup; + } + + /* negation ! operator cannot follow an operand */ + if (ZBX_JSONPATH_TOKEN_OP_NOT == token_type && + ZBX_JSONPATH_TOKEN_GROUP_OPERAND == prev_group) + { + zbx_jsonpath_error(expression + loc.l); + goto cleanup; + } + + for (; 0 < operators.values_num; operators.values_num--) + { + optoken = operators.values[operators.values_num - 1]; + + if (jsonpath_token_precedence(optoken->type) > + jsonpath_token_precedence(token_type)) + { + break; + } + + if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type) + break; + + zbx_vector_ptr_append(&output, optoken); + } + + zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc)); + prev_group = jsonpath_token_group(token_type); + continue; + } + + if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == token_type) + { + zbx_vector_ptr_append(&operators, jsonpath_create_token(token_type, expression, &loc)); + prev_group = ZBX_JSONPATH_TOKEN_GROUP_NONE; + continue; + } + + if (ZBX_JSONPATH_TOKEN_PAREN_RIGHT == token_type) + { + /* right parenthesis must follow and operand or right parenthesis */ + if (ZBX_JSONPATH_TOKEN_GROUP_OPERAND != prev_group) + { + zbx_jsonpath_error(expression + loc.l); + goto cleanup; + } + + for (optoken = 0; 0 < operators.values_num; operators.values_num--) + { + optoken = operators.values[operators.values_num - 1]; + + if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type) + { + operators.values_num--; + break; + } + + zbx_vector_ptr_append(&output, optoken); + } + + if (NULL == optoken) + { + zbx_jsonpath_error(expression + loc.l); + goto cleanup; + } + jsonpath_token_free(optoken); + + prev_group = ZBX_JSONPATH_TOKEN_GROUP_OPERAND; + continue; + } + } +out: + if (SUCCEED == ret) + { + zbx_jsonpath_segment_t *segment; + + for (optoken = 0; 0 < operators.values_num; operators.values_num--) + { + optoken = operators.values[operators.values_num - 1]; + + if (ZBX_JSONPATH_TOKEN_PAREN_LEFT == optoken->type) + { + zbx_set_json_strerror("mismatched () brackets in expression: %s", expression); + ret = FAIL; + goto cleanup; + } + + zbx_vector_ptr_append(&output, optoken); + } + + jsonpath_reserve(jsonpath, 1); + segment = &jsonpath->segments[jsonpath->segments_num++]; + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION; + zbx_vector_ptr_create(&segment->data.expression.tokens); + zbx_vector_ptr_append_array(&segment->data.expression.tokens, output.values, output.values_num); + + jsonpath->definite = 0; + } +cleanup: + if (SUCCEED != ret) + { + zbx_vector_ptr_clear_ext(&operators, (zbx_clean_func_t)jsonpath_token_free); + zbx_vector_ptr_clear_ext(&output, (zbx_clean_func_t)jsonpath_token_free); + } + + zbx_vector_ptr_destroy(&operators); + zbx_vector_ptr_destroy(&output); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_names * + * * + * Purpose: parse a list of single or double quoted names, including trivial * + * case when a single name is used * + * * + * Parameters: list - [IN] the name list * + * jsonpath - [IN/OUT] the jsonpath * + * next - [OUT] a pointer to the next character after parsed * + * list * + * * + * Return value: SUCCEED - the list was parsed successfully * + * FAIL - otherwise * + * * + * Comments: In the trivial case (when list contains one name) the name is * + * stored into zbx_jsonpath_list_t:value field and later its * + * address is stored into zbx_jsonpath_list_t:values to reduce * + * allocations in trivial cases. * + * * + ******************************************************************************/ +static int jsonpath_parse_names(const char *list, zbx_jsonpath_t *jsonpath, const char **next) +{ + zbx_jsonpath_segment_t *segment; + int ret = FAIL, parsed_name = 0; + const char *end, *start = NULL; + zbx_jsonpath_list_node_t *head = NULL; + + for (end = list; ']' != *end || NULL != start; end++) + { + switch (*end) + { + case '\'': + case '"': + if (NULL == start) + { + start = end; + } + else if (*start == *end) + { + zbx_jsonpath_list_node_t *node; + + if (start + 1 == end) + { + ret = zbx_jsonpath_error(start); + goto out; + } + + node = jsonpath_list_create_node(end - start + 1); + jsonpath_unquote(node->data, start, end - start + 1); + node->next = head; + head = node; + parsed_name = 1; + start = NULL; + } + break; + case '\\': + if (NULL == start || ('\\' != end[1] && *start != end[1])) + { + ret = zbx_jsonpath_error(end); + goto out; + } + end++; + break; + case ' ': + case '\t': + break; + case ',': + if (0 == parsed_name) + { + ret = zbx_jsonpath_error(end); + goto out; + } + parsed_name = 0; + break; + case '\0': + ret = zbx_jsonpath_error(end); + goto out; + default: + if (NULL == start) + { + ret = zbx_jsonpath_error(end); + goto out; + } + } + } + + if (0 == parsed_name) + { + ret = zbx_jsonpath_error(end); + goto out; + } + + segment = &jsonpath->segments[jsonpath->segments_num++]; + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; + segment->data.list.type = ZBX_JSONPATH_LIST_NAME; + segment->data.list.values = head; + + if (NULL != head->next) + jsonpath->definite = 0; + + head = NULL; + *next = end; + ret = SUCCEED; +out: + if (NULL != head) + jsonpath_list_free(head); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_indexes * + * * + * Purpose: parse a list of array indexes or range start:end values * + * case when a single name is used * + * * + * Parameters: list - [IN] the index list * + * jsonpath - [IN/OUT] the jsonpath * + * next - [OUT] a pointer to the next character after parsed * + * list * + * * + * Return value: SUCCEED - the list was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_parse_indexes(const char *list, zbx_jsonpath_t *jsonpath, const char **next) +{ + zbx_jsonpath_segment_t *segment; + const char *end, *start = NULL; + int ret = FAIL, type = ZBX_JSONPATH_SEGMENT_UNKNOWN; + unsigned int flags = 0, parsed_index = 0; + zbx_jsonpath_list_node_t *head = NULL, *node; + + for (end = list; ; end++) + { + if (0 != isdigit((unsigned char)*end)) + { + if (NULL == start) + start = end; + continue; + } + + if ('-' == *end) + { + if (NULL != start) + { + ret = zbx_jsonpath_error(end); + goto out; + } + start = end; + continue; + } + + if (NULL != start) + { + int value; + + if ('-' == *start && end == start + 1) + { + ret = zbx_jsonpath_error(start); + goto out; + } + + node = jsonpath_list_create_node(sizeof(int)); + node->next = head; + head = node; + value = atoi(start); + memcpy(node->data, &value, sizeof(int)); + start = NULL; + parsed_index = 1; + } + + if (']' == *end) + { + if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE != type) + { + if (0 == parsed_index) + { + ret = zbx_jsonpath_error(end); + goto out; + } + } + else + flags |= (parsed_index << 1); + break; + } + + if (':' == *end) + { + if (ZBX_JSONPATH_SEGMENT_UNKNOWN != type) + { + ret = zbx_jsonpath_error(end); + goto out; + } + type = ZBX_JSONPATH_SEGMENT_MATCH_RANGE; + flags |= parsed_index; + parsed_index = 0; + } + else if (',' == *end) + { + if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE == type || 0 == parsed_index) + { + ret = zbx_jsonpath_error(end); + goto out; + } + type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; + parsed_index = 0; + } + else if (' ' != *end && '\t' != *end) + { + ret = zbx_jsonpath_error(end); + goto out; + } + } + + segment = &jsonpath->segments[jsonpath->segments_num++]; + + if (ZBX_JSONPATH_SEGMENT_MATCH_RANGE == type) + { + node = head; + + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_RANGE; + segment->data.range.flags = flags; + if (0 != (flags & 0x02)) + { + memcpy(&segment->data.range.end, node->data, sizeof(int)); + node = node->next; + } + else + segment->data.range.end = 0; + + if (0 != (flags & 0x01)) + memcpy(&segment->data.range.start, node->data, sizeof(int)); + else + segment->data.range.start = 0; + + jsonpath->definite = 0; + } + else + { + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; + segment->data.list.type = ZBX_JSONPATH_LIST_INDEX; + segment->data.list.values = head; + + if (NULL != head->next) + jsonpath->definite = 0; + + head = NULL; + } + + *next = end; + ret = SUCCEED; +out: + if (NULL != head) + jsonpath_list_free(head); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_bracket_segment * + * * + * Purpose: parse jsonpath bracket notation segment * + * * + * Parameters: start - [IN] the segment start * + * jsonpath - [IN/OUT] the jsonpath * + * next - [OUT] a pointer to the next character after parsed * + * segment * + * * + * Return value: SUCCEED - the segment was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_parse_bracket_segment(const char *start, zbx_jsonpath_t *jsonpath, const char **next) +{ + const char *ptr = start; + int ret; + + SKIP_WHITESPACE(ptr); + + if ('?' == *ptr) + { + ret = jsonpath_parse_expression(ptr + 1, jsonpath, next); + } + else if ('*' == *ptr) + { + jsonpath->segments[jsonpath->segments_num++].type = ZBX_JSONPATH_SEGMENT_MATCH_ALL; + jsonpath->definite = 0; + *next = ptr + 1; + ret = SUCCEED; + } + else if ('\'' == *ptr || '"' == *ptr) + { + ret = jsonpath_parse_names(ptr, jsonpath, next); + } + else if (0 != isdigit((unsigned char)*ptr) || ':' == *ptr || '-' == *ptr) + { + ret = jsonpath_parse_indexes(ptr, jsonpath, next); + } + else + ret = zbx_jsonpath_error(ptr); + + if (SUCCEED == ret) + { + ptr = *next; + SKIP_WHITESPACE(ptr); + + if (']' != *ptr) + return zbx_jsonpath_error(ptr); + + *next = ptr + 1; + } + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_parse_dot_segment * + * * + * Purpose: parse jsonpath dot notation segment * + * * + * Parameters: start - [IN] the segment start * + * jsonpath - [IN/OUT] the jsonpath * + * next - [OUT] a pointer to the next character after parsed * + * segment * + * * + * Return value: SUCCEED - the segment was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_parse_dot_segment(const char *start, zbx_jsonpath_t *jsonpath, const char **next) +{ + zbx_jsonpath_segment_t *segment; + const char *ptr; + int len; + + segment = &jsonpath->segments[jsonpath->segments_num]; + jsonpath->segments_num++; + + if ('*' == *start) + { + jsonpath->definite = 0; + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_ALL; + *next = start + 1; + return SUCCEED; + } + + for (ptr = start; 0 != isalnum((unsigned char)*ptr) || '_' == *ptr;) + ptr++; + + if ('(' == *ptr) + { + const char *end = ptr + 1; + + SKIP_WHITESPACE(end); + if (')' == *end) + { + if (ZBX_CONST_STRLEN("min") == ptr - start && 0 == strncmp(start, "min", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_MIN; + else if (ZBX_CONST_STRLEN("max") == ptr - start && 0 == strncmp(start, "max", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_MAX; + else if (ZBX_CONST_STRLEN("avg") == ptr - start && 0 == strncmp(start, "avg", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_AVG; + else if (ZBX_CONST_STRLEN("length") == ptr - start && 0 == strncmp(start, "length", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_LENGTH; + else if (ZBX_CONST_STRLEN("first") == ptr - start && 0 == strncmp(start, "first", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_FIRST; + else if (ZBX_CONST_STRLEN("sum") == ptr - start && 0 == strncmp(start, "sum", ptr - start)) + segment->data.function.type = ZBX_JSONPATH_FUNCTION_SUM; + else + return zbx_jsonpath_error(start); + + segment->type = ZBX_JSONPATH_SEGMENT_FUNCTION; + *next = end + 1; + return SUCCEED; + } + } + + if (0 < (len = ptr - start)) + { + segment->type = ZBX_JSONPATH_SEGMENT_MATCH_LIST; + segment->data.list.type = ZBX_JSONPATH_LIST_NAME; + segment->data.list.values = jsonpath_list_create_node(len + 1); + zbx_strlcpy(segment->data.list.values->data, start, len + 1); + segment->data.list.values->next = NULL; + *next = start + len; + return SUCCEED; + } + + return zbx_jsonpath_error(start); +} + +/****************************************************************************** + * * + * Function: jsonpath_pointer_to_jp * + * * + * Purpose: convert a pointer to an object/array/value in json data to * + * json parse structure * + * * + * Parameters: pnext - [IN] a pointer to object/array/value data * + * jp - [OUT] json parse data with start/end set * + * * + * Return value: SUCCEED - pointer was converted successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_pointer_to_jp(const char *pnext, struct zbx_json_parse *jp) +{ + if ('[' == *pnext || '{' == *pnext) + { + return zbx_json_brackets_open(pnext, jp); + } + else + { + jp->start = pnext; + jp->end = pnext + json_parse_value(pnext, NULL) - 1; + return SUCCEED; + } +} + +/****************************************************************************** + * * + * Function: jsonpath_query_contents * + * * + * Purpose: perform the rest of jsonpath query on json data * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to object/array/value in json data * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - the data were queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_query_contents(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects) +{ + struct zbx_json_parse jp_child; + + switch (*pnext) + { + case '{': + if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) + return FAIL; + + return jsonpath_query_object(jp_root, &jp_child, jsonpath, path_depth, objects); + case '[': + if (FAIL == zbx_json_brackets_open(pnext, &jp_child)) + return FAIL; + + return jsonpath_query_array(jp_root, &jp_child, jsonpath, path_depth, objects); + } + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_query_next_segment * + * * + * Purpose: query next segment * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to object/array/value in json data * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - the segment was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_query_next_segment(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects) +{ + /* check if jsonpath end has been reached, so we have found matching data */ + /* (functions are processed afterwards) */ + if (++path_depth == jsonpath->segments_num || + ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath->segments[path_depth].type) + { + zbx_vector_str_append(objects, (char *)pnext); + return SUCCEED; + } + + /* continue by matching found data against the rest of jsonpath segments */ + return jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); +} + +/****************************************************************************** + * * + * Function: jsonpath_match_name * + * * + * Purpose: match object value name against jsonpath segment name list * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to object value with the specified * + * name * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * name - [IN] the object value name * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - no errors, failed match is not an error * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_name(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, const char *name, zbx_vector_str_t *objects) +{ + zbx_jsonpath_segment_t *segment; + zbx_jsonpath_list_node_t *node; + + segment = &jsonpath->segments[path_depth]; + + /* object contents can match only name list */ + if (ZBX_JSONPATH_LIST_NAME != segment->data.list.type) + return SUCCEED; + + for (node = segment->data.list.values; NULL != node; node = node->next) + { + if (0 == strcmp(name, node->data)) + { + if (FAIL == jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects)) + return FAIL; + break; + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_extract_value * + * * + * Purpose: extract value from json data by the specified path * + * * + * Parameters: jp - [IN] the parent object * + * path - [IN] the jsonpath (definite) * + * value - [OUT] the extracted value * + * * + * Return value: SUCCEED - the value was extracted successfully * + * FAIL - in the case of errors or if there was no value to * + * extract * + * * + ******************************************************************************/ +static int jsonpath_extract_value(const struct zbx_json_parse *jp, const char *path, zbx_variant_t *value) +{ + struct zbx_json_parse jp_child; + char *data = NULL, *tmp_path = NULL; + size_t data_alloc = 0; + int ret = FAIL; + + if ('@' == *path) + { + tmp_path = zbx_strdup(NULL, path); + *tmp_path = '$'; + path = tmp_path; + } + + if (FAIL == zbx_json_open_path(jp, path, &jp_child)) + goto out; + + if (NULL == zbx_json_decodevalue_dyn(jp_child.start, &data, &data_alloc, NULL)) + { + size_t len = jp_child.end - jp_child.start + 2; + + data = (char *)zbx_malloc(NULL, len); + zbx_strlcpy(data, jp_child.start, len); + } + + zbx_variant_set_str(value, data); + ret = SUCCEED; +out: + zbx_free(tmp_path); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_expression_to_str * + * * + * Purpose: convert jsonpath expression to text format * + * * + * Parameters: expression - [IN] the jsonpath exprssion * + * * + * Return value: The converted expression, must be freed by the caller. * + * * + * Comments: This function is used to include expression in error message. * + * * + ******************************************************************************/ +static char *jsonpath_expression_to_str(zbx_jsonpath_expression_t *expression) +{ + int i; + char *str = NULL; + size_t str_alloc = 0, str_offset = 0; + + for (i = 0; i < expression->tokens.values_num; i++) + { + zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)expression->tokens.values[i]; + + if (0 != i) + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ","); + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_CONST_STR: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_CONST_NUM: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, token->data); + break; + case ZBX_JSONPATH_TOKEN_PAREN_LEFT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "("); + break; + case ZBX_JSONPATH_TOKEN_PAREN_RIGHT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ")"); + break; + case ZBX_JSONPATH_TOKEN_OP_PLUS: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "+"); + break; + case ZBX_JSONPATH_TOKEN_OP_MINUS: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "-"); + break; + case ZBX_JSONPATH_TOKEN_OP_MULT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "*"); + break; + case ZBX_JSONPATH_TOKEN_OP_DIV: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "/"); + break; + case ZBX_JSONPATH_TOKEN_OP_EQ: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "=="); + break; + case ZBX_JSONPATH_TOKEN_OP_NE: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "!="); + break; + case ZBX_JSONPATH_TOKEN_OP_GT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ">"); + break; + case ZBX_JSONPATH_TOKEN_OP_GE: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, ">="); + break; + case ZBX_JSONPATH_TOKEN_OP_LT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "<"); + break; + case ZBX_JSONPATH_TOKEN_OP_LE: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "<="); + break; + case ZBX_JSONPATH_TOKEN_OP_NOT: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "!"); + break; + case ZBX_JSONPATH_TOKEN_OP_AND: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "&&"); + break; + case ZBX_JSONPATH_TOKEN_OP_OR: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "||"); + break; + case ZBX_JSONPATH_TOKEN_OP_REGEXP: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "=~"); + break; + default: + zbx_strcpy_alloc(&str, &str_alloc, &str_offset, "?"); + break; + } + } + + return str; +} + +/****************************************************************************** + * * + * Function: jsonpath_set_expression_error * + * * + * Purpose: set jsonpath expression error message * + * * + * Parameters: expression - [IN] the jsonpath exprssion * + * * + * Comments: This function is used to set error message when expression * + * evaluation fails * + * * + ******************************************************************************/ +static void jsonpath_set_expression_error(zbx_jsonpath_expression_t *expression) +{ + char *text; + + text = jsonpath_expression_to_str(expression); + zbx_set_json_strerror("invalid compiled expression: %s", text); + zbx_free(text); +} + +/****************************************************************************** + * * + * Function: jsonpath_variant_to_boolean * + * * + * Purpose: convert variant value to 'boolean' (1, 0) * + * * + * Parameters: value - [IN/OUT] the value * + * * + * Comments: This function is used to cast operand to boolean value for * + * boolean functions (and, or, negation). * + * * + ******************************************************************************/ +static void jsonpath_variant_to_boolean(zbx_variant_t *value) +{ + double res; + + switch (value->type) + { + case ZBX_VARIANT_UI64: + res = (0 != value->data.ui64 ? 1 : 0); + break; + case ZBX_VARIANT_DBL: + res = (SUCCEED != zbx_double_compare(value->data.dbl, 0.0) ? 1 : 0); + break; + case ZBX_VARIANT_STR: + res = ('\0' != *value->data.str ? 1 : 0); + break; + case ZBX_VARIANT_NONE: + res = 0; + break; + default: + THIS_SHOULD_NEVER_HAPPEN; + res = 0; + break; + } + + zbx_variant_clear(value); + zbx_variant_set_dbl(value, res); +} + +/****************************************************************************** + * * + * Function: jsonpath_regexp_match * + * * + * Purpose: match text against regular expression * + * * + * Parameters: text - [IN] the text to match * + * pattern - [IN] the regular expression * + * result - [OUT] 1.0 if match succeeded, 0.0 otherwise * + * * + * Return value: SUCCEED - regular expression match was performed * + * FAIL - regular expression error * + * * + ******************************************************************************/ +static int jsonpath_regexp_match(const char *text, const char *pattern, double *result) +{ + zbx_regexp_t *rxp; + const char *error = NULL; + + if (FAIL == zbx_regexp_compile(pattern, &rxp, &error)) + { + zbx_set_json_strerror("invalid regular expression in JSON path: %s", error); + return FAIL; + } + *result = (0 == zbx_regexp_match_precompiled(text, rxp) ? 1.0 : 0.0); + zbx_regexp_free(rxp); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_match_expression * + * * + * Purpose: match json array element/object value against jsonpath expression * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to array element/object value * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - no errors, failed match is not an error * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_expression(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects) +{ + struct zbx_json_parse jp; + zbx_vector_var_t stack; + int i, ret = SUCCEED; + zbx_jsonpath_segment_t *segment; + zbx_variant_t value, *right; + double res; + + if (SUCCEED != jsonpath_pointer_to_jp(pnext, &jp)) + return FAIL; + + zbx_vector_var_create(&stack); + + segment = &jsonpath->segments[path_depth]; + + for (i = 0; i < segment->data.expression.tokens.values_num; i++) + { + zbx_variant_t *left; + zbx_jsonpath_token_t *token = (zbx_jsonpath_token_t *)segment->data.expression.tokens.values[i]; + + if (ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2 == jsonpath_token_group(token->type)) + { + if (2 > stack.values_num) + { + jsonpath_set_expression_error(&segment->data.expression); + ret = FAIL; + goto out; + } + + left = &stack.values[stack.values_num - 2]; + right = &stack.values[stack.values_num - 1]; + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_OP_PLUS: + zbx_variant_convert(left, ZBX_VARIANT_DBL); + zbx_variant_convert(right, ZBX_VARIANT_DBL); + left->data.dbl += right->data.dbl; + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_MINUS: + zbx_variant_convert(left, ZBX_VARIANT_DBL); + zbx_variant_convert(right, ZBX_VARIANT_DBL); + left->data.dbl -= right->data.dbl; + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_MULT: + zbx_variant_convert(left, ZBX_VARIANT_DBL); + zbx_variant_convert(right, ZBX_VARIANT_DBL); + left->data.dbl *= right->data.dbl; + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_DIV: + zbx_variant_convert(left, ZBX_VARIANT_DBL); + zbx_variant_convert(right, ZBX_VARIANT_DBL); + left->data.dbl /= right->data.dbl; + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_EQ: + res = (0 == zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_NE: + res = (0 != zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_GT: + res = (0 < zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_GE: + res = (0 <= zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_LT: + res = (0 > zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_LE: + res = (0 >= zbx_variant_compare(left, right) ? 1.0 : 0.0); + zbx_variant_clear(left); + zbx_variant_clear(right); + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_AND: + jsonpath_variant_to_boolean(left); + jsonpath_variant_to_boolean(right); + if (SUCCEED != zbx_double_compare(left->data.dbl, 0.0) && + SUCCEED != zbx_double_compare(right->data.dbl, 0.0)) + { + res = 1.0; + } + else + res = 0.0; + zbx_variant_set_dbl(left, res); + zbx_variant_clear(right); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_OR: + jsonpath_variant_to_boolean(left); + jsonpath_variant_to_boolean(right); + if (SUCCEED != zbx_double_compare(left->data.dbl, 0.0) || + SUCCEED != zbx_double_compare(right->data.dbl, 0.0)) + { + res = 1.0; + } + else + res = 0.0; + zbx_variant_set_dbl(left, res); + zbx_variant_clear(right); + stack.values_num--; + break; + case ZBX_JSONPATH_TOKEN_OP_REGEXP: + zbx_variant_convert(left, ZBX_VARIANT_STR); + zbx_variant_convert(right, ZBX_VARIANT_STR); + ret = jsonpath_regexp_match(left->data.str, right->data.str, &res); + zbx_variant_clear(left); + zbx_variant_clear(right); + + if (FAIL == ret) + goto out; + + zbx_variant_set_dbl(left, res); + stack.values_num--; + break; + default: + break; + } + continue; + } + + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + if (FAIL == jsonpath_extract_value(jp_root, token->data, &value)) + zbx_variant_set_none(&value); + zbx_vector_var_append_ptr(&stack, &value); + break; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + /* relative path can be applied only to array or object */ + if ('[' != *jp.start && '{' != *jp.start) + goto out; + + if (FAIL == jsonpath_extract_value(&jp, token->data, &value)) + zbx_variant_set_none(&value); + zbx_vector_var_append_ptr(&stack, &value); + break; + case ZBX_JSONPATH_TOKEN_CONST_STR: + zbx_variant_set_str(&value, zbx_strdup(NULL, token->data)); + zbx_vector_var_append_ptr(&stack, &value); + break; + case ZBX_JSONPATH_TOKEN_CONST_NUM: + zbx_variant_set_dbl(&value, atof(token->data)); + zbx_vector_var_append_ptr(&stack, &value); + break; + case ZBX_JSONPATH_TOKEN_OP_NOT: + if (1 > stack.values_num) + { + jsonpath_set_expression_error(&segment->data.expression); + ret = FAIL; + goto out; + } + right = &stack.values[stack.values_num - 1]; + jsonpath_variant_to_boolean(right); + right->data.dbl = 1 - right->data.dbl; + break; + default: + break; + } + } + + if (1 != stack.values_num) + { + jsonpath_set_expression_error(&segment->data.expression); + goto out; + } + + jsonpath_variant_to_boolean(&stack.values[0]); + if (SUCCEED != zbx_double_compare(stack.values[0].data.dbl, 0.0)) + ret = jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects); +out: + for (i = 0; i < stack.values_num; i++) + zbx_variant_clear(&stack.values[i]); + zbx_vector_var_destroy(&stack); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_query_object * + * * + * Purpose: query object fields for jsonpath segment match * + * * + * Parameters: jp_root - [IN] the document root * + * jp - [IN] the json object to query * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - the object was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_query_object(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects) +{ + const char *pnext = NULL; + char name[MAX_STRING_LEN]; + zbx_jsonpath_segment_t *segment; + int ret = SUCCEED; + + segment = &jsonpath->segments[path_depth]; + + while (NULL != (pnext = zbx_json_pair_next(jp, pnext, name, sizeof(name))) && SUCCEED == ret) + { + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_ALL: + ret = jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + ret = jsonpath_match_name(jp_root, pnext, jsonpath, path_depth, name, objects); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + ret = jsonpath_match_expression(jp_root, pnext, jsonpath, path_depth, objects); + break; + default: + break; + } + + if (1 == segment->detached) + ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); + } + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_match_index * + * * + * Purpose: match array element against segment index list * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to an array element * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * index - [IN] the array element index * + * elements_num - [IN] the total number of elements in array * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - no errors, failed match is not an error * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_index(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_str_t *objects) +{ + zbx_jsonpath_segment_t *segment; + zbx_jsonpath_list_node_t *node; + + segment = &jsonpath->segments[path_depth]; + + /* array contents can match only index list */ + if (ZBX_JSONPATH_LIST_INDEX != segment->data.list.type) + return SUCCEED; + + for (node = segment->data.list.values; NULL != node; node = node->next) + { + int query_index; + + memcpy(&query_index, node->data, sizeof(query_index)); + + if ((query_index >= 0 && index == query_index) || index == elements_num + query_index) + { + if (FAIL == jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects)) + return FAIL; + break; + } + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_match_range * + * * + * Purpose: match array element against segment index range * + * * + * Parameters: jp_root - [IN] the document root * + * pnext - [IN] a pointer to an array element * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * index - [IN] the array element index * + * elements_num - [IN] the total number of elements in array * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - no errors, failed match is not an error * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_match_range(const struct zbx_json_parse *jp_root, const char *pnext, + const zbx_jsonpath_t *jsonpath, int path_depth, int index, int elements_num, zbx_vector_str_t *objects) +{ + int start_index, end_index; + zbx_jsonpath_segment_t *segment; + + segment = &jsonpath->segments[path_depth]; + + start_index = (0 != (segment->data.range.flags & 0x01) ? segment->data.range.start : 0); + end_index = (0 != (segment->data.range.flags & 0x02) ? segment->data.range.end : elements_num); + + if (0 > start_index) + start_index += elements_num; + if (0 > end_index) + end_index += elements_num; + + if (start_index <= index && end_index > index) + { + if (FAIL == jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects)) + return FAIL; + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_query_array * + * * + * Purpose: query array elements for jsonpath segment match * + * * + * Parameters: jp_root - [IN] the document root * + * jp - [IN] the json array to query * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * objects - [OUT] pointers to the matched objects in json * + * data * + * * + * Return value: SUCCEED - the array was queried successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +static int jsonpath_query_array(const struct zbx_json_parse *jp_root, const struct zbx_json_parse *jp, + const zbx_jsonpath_t *jsonpath, int path_depth, zbx_vector_str_t *objects) +{ + const char *pnext = NULL; + int index = 0, elements_num = 0, ret = SUCCEED; + zbx_jsonpath_segment_t *segment; + + segment = &jsonpath->segments[path_depth]; + + while (NULL != (pnext = zbx_json_next(jp, pnext))) + elements_num++; + + while (NULL != (pnext = zbx_json_next(jp, pnext)) && SUCCEED == ret) + { + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_ALL: + ret = jsonpath_query_next_segment(jp_root, pnext, jsonpath, path_depth, objects); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + ret = jsonpath_match_index(jp_root, pnext, jsonpath, path_depth, index, + elements_num, objects); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: + ret = jsonpath_match_range(jp_root, pnext, jsonpath, path_depth, index, + elements_num, objects); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + ret = jsonpath_match_expression(jp_root, pnext, jsonpath, path_depth, objects); + break; + default: + break; + } + + if (1 == segment->detached) + ret = jsonpath_query_contents(jp_root, pnext, jsonpath, path_depth, objects); + + index++; + } + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_extract_element * + * * + * Purpose: extract JSON element value from data * + * * + * Parameters: ptr - [IN] pointer to the element to extract * + * element - [OUT] the extracted element * + * * + * Return value: SUCCEED - the element was extracted successfully * + * FAIL - the pointer was not pointing to a JSON element * + * * + * Comments: String value element is unquoted, other elements are copied as * + * is. * + * * + ******************************************************************************/ +static int jsonpath_extract_element(const char *ptr, char **element) +{ + size_t element_size = 0; + + if (NULL == zbx_json_decodevalue_dyn(ptr, element, &element_size, NULL)) + { + struct zbx_json_parse jp; + + if (SUCCEED != zbx_json_brackets_open(ptr, &jp)) + return FAIL; + + *element = jsonpath_strndup(jp.start, jp.end - jp.start + 1); + } + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_extract_numeric_value * + * * + * Purpose: extract numeric value from json data * + * * + * Parameters: ptr - [IN] pointer to the value to extract * + * value - [OUT] the extracted value * + * * + * Return value: SUCCEED - the value was extracted successfully * + * FAIL - the pointer was not pointing at numeric value * + * * + ******************************************************************************/ +static int jsonpath_extract_numeric_value(const char *ptr, double *value) +{ + char buffer[MAX_STRING_LEN]; + + if (('-' != *ptr && 0 == isdigit((unsigned char)*ptr)) || + NULL == zbx_json_decodevalue(ptr, buffer, sizeof(buffer), NULL)) + { + zbx_set_json_strerror("array value is not a number starting with: %s", ptr); + return FAIL; + } + + *value = atof(buffer); + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: jsonpath_apply_function * + * * + * Purpose: apply jsonpath function to the extracted object list * + * * + * Parameters: objects - [IN] pointers to the objects/arrays/values in * + * json data * + * type - [IN] the function type * + * definite_path - [IN] 1 - if the path is definite (pointing at * + * single object) * + * 0 - otherwise * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the function was applied successfully * + * FAIL - invalid input data for the function or internal * + * json error * + * * + ******************************************************************************/ +static int jsonpath_apply_function(const zbx_vector_str_t *objects, zbx_jsonpath_function_type_t type, + int definite_path, char **output) +{ + int i, ret = FAIL; + zbx_vector_str_t objects_tmp; + double result; + + zbx_vector_str_create(&objects_tmp); + + /* convert definite path result to object array if possible */ + if (0 != definite_path) + { + const char *pnext; + struct zbx_json_parse jp; + + if (0 == objects->values_num || '[' != *objects->values[0]) + { + /* all functions can be applied only to arrays */ + /* attempt to apply a function to non-array will fail */ + zbx_set_json_strerror("cannot apply function to non-array JSON element"); + goto out; + } + + if (FAIL == zbx_json_brackets_open(objects->values[0], &jp)) + goto out; + + for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));) + zbx_vector_str_append(&objects_tmp, (char*)pnext); + + objects = &objects_tmp; + } + + if (ZBX_JSONPATH_FUNCTION_LENGTH == type) + { + *output = zbx_dsprintf(NULL, "%d", objects->values_num); + ret = SUCCEED; + goto out; + } + + if (ZBX_JSONPATH_FUNCTION_FIRST == type) + { + if (0 < objects->values_num) + ret = jsonpath_extract_element(objects->values[0], output); + else + ret = SUCCEED; + + goto out; + } + + if (0 == objects->values_num) + { + zbx_set_json_strerror("cannot apply aggregation function to empty array"); + goto out; + } + + if (FAIL == jsonpath_extract_numeric_value(objects->values[0], &result)) + goto out; + + for (i = 1; i < objects->values_num; i++) + { + double value; + + if (FAIL == jsonpath_extract_numeric_value(objects->values[i], &value)) + goto out; + + switch (type) + { + case ZBX_JSONPATH_FUNCTION_MIN: + if (value < result) + result = value; + break; + case ZBX_JSONPATH_FUNCTION_MAX: + if (value > result) + result = value; + break; + case ZBX_JSONPATH_FUNCTION_AVG: + case ZBX_JSONPATH_FUNCTION_SUM: + result += value; + break; + default: + break; + } + } + + if (ZBX_JSONPATH_FUNCTION_AVG == type) + result /= objects->values_num; + + *output = zbx_dsprintf(NULL, ZBX_FS_DBL, result); + del_zeros(*output); + ret = SUCCEED; +out: + zbx_vector_str_destroy(&objects_tmp); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_apply_functions * + * * + * Purpose: apply jsonpath function to the extracted object list * + * * + * Parameters: jp_root - [IN] the document root * + * objects - [IN] pointers to the objects/arrays/values in json* + * data * + * jsonpath - [IN] the jsonpath * + * path_depth - [IN] the jsonpath segment to match * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the function was applied successfully * + * FAIL - invalid input data for the function or internal * + * json error * + * * + ******************************************************************************/ +static int jsonpath_apply_functions(const struct zbx_json_parse *jp_root, const zbx_vector_str_t *objects, + const zbx_jsonpath_t *jsonpath, int path_depth, char **output) +{ + int ret, clear_input_contents = 0, definite_path; + zbx_vector_str_t input; + + zbx_vector_str_create(&input); + + /* when functions are applied directly to the json document (at the start of the jsonpath ) */ + /* it makes all document as input object */ + if (0 == path_depth) + zbx_vector_str_append(&input, (char *)jp_root->start); + else + zbx_vector_str_append_array(&input, objects->values, objects->values_num); + + definite_path = jsonpath->definite; + + while (SUCCEED == (ret = jsonpath_apply_function(&input, jsonpath->segments[path_depth].data.function.type, + definite_path, output))) + { + if (++path_depth == jsonpath->segments_num) + break; + + if (0 != clear_input_contents) + zbx_vector_str_clear_ext(&input, zbx_str_free); + else + zbx_vector_str_clear(&input); + + zbx_vector_str_append(&input, *output); + *output = NULL; + clear_input_contents = 1; + + /* functions return single value, so for the next functions path becomes definite */ + definite_path = 1; + } + + if (0 != clear_input_contents) + zbx_vector_str_clear_ext(&input, zbx_str_free); + zbx_vector_str_destroy(&input); + + return ret; +} + +/****************************************************************************** + * * + * Function: jsonpath_format_query_result * + * * + * Purpose: format query result, depending on jsonpath type * + * * + * Parameters: objects - [IN] pointers to the objects/arrays/values in json * + * data * + * jsonpath - [IN] the jsonpath used to acquire result * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the result was formatted successfully * + * FAIL - invalid result data (internal json error) * + * * + ******************************************************************************/ +static int jsonpath_format_query_result(const zbx_vector_str_t *objects, zbx_jsonpath_t *jsonpath, char **output) +{ + size_t output_offset = 0, output_alloc; + int i; + + if (0 == objects->values_num) + return SUCCEED; + + if (1 == jsonpath->definite) + { + return jsonpath_extract_element(objects->values[0], output); + } + + /* reserve 32 bytes per returned object plus array start/end [] and terminating zero */ + output_alloc = objects->values_num * 32 + 3; + *output = (char *)zbx_malloc(NULL, output_alloc); + + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, '['); + + for (i = 0; i < objects->values_num; i++) + { + struct zbx_json_parse jp; + + if (FAIL == jsonpath_pointer_to_jp(objects->values[i], &jp)) + { + zbx_set_json_strerror("cannot format query result, unrecognized json part starting with: %s", + objects->values[i]); + zbx_free(*output); + return FAIL; + } + + if (0 != i) + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ','); + + zbx_strncpy_alloc(output, &output_alloc, &output_offset, jp.start, jp.end - jp.start + 1); + } + + zbx_chrcpy_alloc(output, &output_alloc, &output_offset, ']'); + + return SUCCEED; +} + +/****************************************************************************** + * * + * Function: zbx_jsonpath_clear * + * * + ******************************************************************************/ +void zbx_jsonpath_clear(zbx_jsonpath_t *jsonpath) +{ + int i; + + for (i = 0; i < jsonpath->segments_num; i++) + jsonpath_segment_clear(&jsonpath->segments[i]); + + zbx_free(jsonpath->segments); +} + +/****************************************************************************** + * * + * Function: zbx_jsonpath_compile * + * * + * Purpose: compile jsonpath to be used in queries * + * * + * Parameters: path - [IN] the path to parse * + * jsonpath - [IN/OUT] the compiled jsonpath * + * * + * Return value: SUCCEED - the segment was parsed successfully * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_jsonpath_compile(const char *path, zbx_jsonpath_t *jsonpath) +{ + int ret = FAIL; + const char *ptr = path, *next; + zbx_jsonpath_segment_type_t segment_type, last_segment_type = ZBX_JSONPATH_SEGMENT_UNKNOWN; + zbx_jsonpath_t jpquery; + + if ('$' != *ptr || '\0' == ptr[1]) + { + zbx_set_json_strerror("JSONPath query must start with the root object/element $."); + return FAIL; + } + + memset(&jpquery, 0, sizeof(zbx_jsonpath_t)); + jsonpath_reserve(&jpquery, 4); + jpquery.definite = 1; + + for (ptr++; '\0' != *ptr; ptr = next) + { + char prefix; + + jsonpath_reserve(&jpquery, 1); + + if ('.' == (prefix = *ptr)) + { + if ('.' == *(++ptr)) + { + /* mark next segment as detached */ + zbx_jsonpath_segment_t *segment = &jpquery.segments[jpquery.segments_num]; + + if (1 != segment->detached) + { + segment->detached = 1; + jpquery.definite = 0; + ptr++; + } + } + + switch (*ptr) + { + case '[': + prefix = *ptr; + break; + case '\0': + case '.': + prefix = 0; + break; + } + } + + switch (prefix) + { + case '.': + ret = jsonpath_parse_dot_segment(ptr, &jpquery, &next); + break; + case '[': + ret = jsonpath_parse_bracket_segment(ptr + 1, &jpquery, &next); + break; + default: + ret = zbx_jsonpath_error(ptr); + break; + } + + if (SUCCEED != ret) + break; + + /* function segments can followed only by function segments */ + segment_type = jpquery.segments[jpquery.segments_num - 1].type; + if (ZBX_JSONPATH_SEGMENT_FUNCTION == last_segment_type && ZBX_JSONPATH_SEGMENT_FUNCTION != segment_type) + { + ret = zbx_jsonpath_error(ptr); + break; + } + last_segment_type = segment_type; + } + + if (SUCCEED == ret && 0 == jpquery.segments_num) + ret = zbx_jsonpath_error(ptr); + + if (SUCCEED == ret) + *jsonpath = jpquery; + else + zbx_jsonpath_clear(&jpquery); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_jsonpath_query * + * * + * Purpose: perform jsonpath query on the specified json data * + * * + * Parameters: jp - [IN] the json data * + * path - [IN] the jsonpath * + * output - [OUT] the output value * + * * + * Return value: SUCCEED - the query was performed successfully (empty result * + * being counted as successful query) * + * FAIL - otherwise * + * * + ******************************************************************************/ +int zbx_jsonpath_query(const struct zbx_json_parse *jp, const char *path, char **output) +{ + zbx_jsonpath_t jsonpath; + int path_depth = 0, ret = SUCCEED; + zbx_vector_str_t objects; + + if (FAIL == zbx_jsonpath_compile(path, &jsonpath)) + return FAIL; + + zbx_vector_str_create(&objects); + + if ('{' == *jp->start) + ret = jsonpath_query_object(jp, jp, &jsonpath, path_depth, &objects); + else if ('[' == *jp->start) + ret = jsonpath_query_array(jp, jp, &jsonpath, path_depth, &objects); + + if (SUCCEED == ret) + { + path_depth = jsonpath.segments_num; + while (0 < path_depth && ZBX_JSONPATH_SEGMENT_FUNCTION == jsonpath.segments[path_depth - 1].type) + path_depth--; + + if (path_depth < jsonpath.segments_num) + ret = jsonpath_apply_functions(jp, &objects, &jsonpath, path_depth, output); + else + ret = jsonpath_format_query_result(&objects, &jsonpath, output); + } + + zbx_vector_str_destroy(&objects); + zbx_jsonpath_clear(&jsonpath); + + return ret; +} diff --git a/src/libs/zbxjson/jsonpath.h b/src/libs/zbxjson/jsonpath.h new file mode 100644 index 00000000000..450d4e4d3fb --- /dev/null +++ b/src/libs/zbxjson/jsonpath.h @@ -0,0 +1,159 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#ifndef ZABBIX_JSONPATH_H +#define ZABBIX_JSONPATH_H + +#include "zbxalgo.h" + +typedef enum +{ + ZBX_JSONPATH_SEGMENT_UNKNOWN, + ZBX_JSONPATH_SEGMENT_MATCH_ALL, + ZBX_JSONPATH_SEGMENT_MATCH_LIST, + ZBX_JSONPATH_SEGMENT_MATCH_RANGE, + ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION, + ZBX_JSONPATH_SEGMENT_FUNCTION +} +zbx_jsonpath_segment_type_t; + +/* specifies if the match list contains object property names or array indices */ +typedef enum +{ + ZBX_JSONPATH_LIST_NAME = 1, + ZBX_JSONPATH_LIST_INDEX +} +zbx_jsonpath_list_type_t; + +typedef enum +{ + ZBX_JSONPATH_FUNCTION_MIN = 1, + ZBX_JSONPATH_FUNCTION_MAX, + ZBX_JSONPATH_FUNCTION_AVG, + ZBX_JSONPATH_FUNCTION_LENGTH, + ZBX_JSONPATH_FUNCTION_FIRST, + ZBX_JSONPATH_FUNCTION_SUM +} +zbx_jsonpath_function_type_t; + +typedef struct zbx_jsonpath_list_item +{ + struct zbx_jsonpath_list_item *next; + /* the structure is always over-allocated so that either int */ + /* or a zero terminated string can be stored in data */ + char data[1]; +} +zbx_jsonpath_list_node_t; + +typedef struct +{ + zbx_jsonpath_list_node_t *values; + zbx_jsonpath_list_type_t type; +} +zbx_jsonpath_list_t; + +typedef struct +{ + int start; + int end; + unsigned int flags; +} +zbx_jsonpath_range_t; + +/* expression tokens in postfix notation */ +typedef struct +{ + zbx_vector_ptr_t tokens; +} +zbx_jsonpath_expression_t; + +typedef struct +{ + zbx_jsonpath_function_type_t type; +} +zbx_jsonpath_function_t; + +typedef union +{ + zbx_jsonpath_list_t list; + zbx_jsonpath_range_t range; + zbx_jsonpath_expression_t expression; + zbx_jsonpath_function_t function; +} +zbx_jsonpath_data_t; + +struct zbx_jsonpath_segment +{ + zbx_jsonpath_segment_type_t type; + zbx_jsonpath_data_t data; + + /* set to 1 if the segment is 'detached' and can be anywhere in parent node tree */ + unsigned char detached; +}; + +/* */ +/* Token groups: */ +/* operand - constant value, jsonpath reference, result of () evaluation */ +/* operator2 - binary operator (arithmetic or comparison) */ +/* operator1 - unary operator (negation !) */ +/* */ +typedef enum +{ + ZBX_JSONPATH_TOKEN_GROUP_NONE, + ZBX_JSONPATH_TOKEN_GROUP_OPERAND, + ZBX_JSONPATH_TOKEN_GROUP_OPERATOR2, /* binary operator */ + ZBX_JSONPATH_TOKEN_GROUP_OPERATOR1 /* unary operator */ +} +zbx_jsonpath_token_group_t; + +/* expression token types */ +typedef enum +{ + ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE = 1, + ZBX_JSONPATH_TOKEN_PATH_RELATIVE, + ZBX_JSONPATH_TOKEN_CONST_STR, + ZBX_JSONPATH_TOKEN_CONST_NUM, + ZBX_JSONPATH_TOKEN_PAREN_LEFT, + ZBX_JSONPATH_TOKEN_PAREN_RIGHT, + ZBX_JSONPATH_TOKEN_OP_PLUS, + ZBX_JSONPATH_TOKEN_OP_MINUS, + ZBX_JSONPATH_TOKEN_OP_MULT, + ZBX_JSONPATH_TOKEN_OP_DIV, + ZBX_JSONPATH_TOKEN_OP_EQ, + ZBX_JSONPATH_TOKEN_OP_NE, + ZBX_JSONPATH_TOKEN_OP_GT, + ZBX_JSONPATH_TOKEN_OP_GE, + ZBX_JSONPATH_TOKEN_OP_LT, + ZBX_JSONPATH_TOKEN_OP_LE, + ZBX_JSONPATH_TOKEN_OP_NOT, + ZBX_JSONPATH_TOKEN_OP_AND, + ZBX_JSONPATH_TOKEN_OP_OR, + ZBX_JSONPATH_TOKEN_OP_REGEXP +} +zbx_jsonpath_token_type_t; + +typedef struct +{ + unsigned char type; + char *data; +} +zbx_jsonpath_token_t; + + +#endif diff --git a/src/libs/zbxserver/expression.c b/src/libs/zbxserver/expression.c index 7e1d31bddd3..dc9bb1a74cf 100644 --- a/src/libs/zbxserver/expression.c +++ b/src/libs/zbxserver/expression.c @@ -5205,7 +5205,6 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c { char c, *replace_to = NULL; int ret = SUCCEED, l ,r; - size_t replace_to_alloc = 0; if (ZBX_TOKEN_LLD_FUNC_MACRO == token->type) { @@ -5221,7 +5220,7 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c c = (*data)[r + 1]; (*data)[r + 1] = '\0'; - if (SUCCEED != zbx_lld_macro_value_by_name(jp_row, lld_macro_paths, *data + l, &replace_to, &replace_to_alloc)) + if (SUCCEED != zbx_lld_macro_value_by_name(jp_row, lld_macro_paths, *data + l, &replace_to)) { zabbix_log(LOG_LEVEL_DEBUG, "cannot substitute macro \"%s\": not found in value set", *data + l); @@ -5241,7 +5240,6 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c if (ZBX_TOKEN_LLD_FUNC_MACRO == token->type) { - replace_to_alloc = 0; if (SUCCEED != (zbx_calculate_macro_function(*data, &token->data.lld_func_macro, &replace_to))) { int len = token->data.lld_func_macro.func.r - token->data.lld_func_macro.func.l + 1; @@ -5266,6 +5264,9 @@ static int process_lld_macro_token(char **data, zbx_token_t *token, int flags, c { if (SUCCEED == is_double_suffix(replace_to, ZBX_FLAG_DOUBLE_SUFFIX)) { + size_t replace_to_alloc; + + replace_to_alloc = strlen(replace_to) + 1; wrap_negative_double_suffix(&replace_to, &replace_to_alloc); } else diff --git a/src/zabbix_agent/Makefile.am b/src/zabbix_agent/Makefile.am index 32d58ef5de9..79945f77bc6 100644 --- a/src/zabbix_agent/Makefile.am +++ b/src/zabbix_agent/Makefile.am @@ -39,9 +39,9 @@ zabbix_agentd_LDADD = \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ - $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxmodules/libzbxmodules.a \ diff --git a/src/zabbix_sender/Makefile.am b/src/zabbix_sender/Makefile.am index 6d39308d8cf..c7395df0754 100644 --- a/src/zabbix_sender/Makefile.am +++ b/src/zabbix_sender/Makefile.am @@ -5,6 +5,9 @@ bin_PROGRAMS = zabbix_sender zabbix_sender_SOURCES = zabbix_sender.c zabbix_sender_LDADD = \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ @@ -12,7 +15,6 @@ zabbix_sender_LDADD = \ $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ - $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a zabbix_sender_LDADD += @SENDER_LIBS@ diff --git a/src/zabbix_server/lld/lld.c b/src/zabbix_server/lld/lld.c index 824028047bb..0f3348df142 100644 --- a/src/zabbix_server/lld/lld.c +++ b/src/zabbix_server/lld/lld.c @@ -224,11 +224,9 @@ static int filter_condition_match(const struct zbx_json_parse *jp_row, const zbx const lld_condition_t *condition) { char *value = NULL; - size_t value_alloc = 0; int ret; - if (SUCCEED == (ret = zbx_lld_macro_value_by_name(jp_row, lld_macro_paths, condition->macro, &value, - &value_alloc))) + if (SUCCEED == (ret = zbx_lld_macro_value_by_name(jp_row, lld_macro_paths, condition->macro, &value))) { switch (regexp_match_ex(&condition->regexps, value, condition->regexp, ZBX_CASE_SENSITIVE)) { @@ -480,7 +478,7 @@ static void lld_check_received_data_for_filter(lld_filter_t *filter, const struc { int i, index; zbx_lld_macro_path_t lld_macro_path_local, *lld_macro_path; - struct zbx_json_parse jp_out; + char *output = NULL; for (i = 0; i < filter->conditions.values_num; i++) { @@ -493,12 +491,13 @@ static void lld_check_received_data_for_filter(lld_filter_t *filter, const struc { lld_macro_path = (zbx_lld_macro_path_t *)lld_macro_paths->values[index]; - if (FAIL == zbx_json_path_open(jp_row, lld_macro_path->path, &jp_out)) + if (FAIL == zbx_jsonpath_query(jp_row, lld_macro_path->path, &output) || NULL == output) { *info = zbx_strdcatf(*info, "Cannot accurately apply filter: no value received for macro \"%s\"" " json path '%s'.\n", lld_macro_path->lld_macro, lld_macro_path->path); } + zbx_free(output); continue; } diff --git a/src/zabbix_server/lld/lld_item.c b/src/zabbix_server/lld/lld_item.c index cbbb08ebf11..bc5738c0e47 100644 --- a/src/zabbix_server/lld/lld_item.c +++ b/src/zabbix_server/lld/lld_item.c @@ -1347,6 +1347,7 @@ static int lld_items_preproc_step_validate(const zbx_lld_item_preproc_t * pp, zb char param1[ITEM_PREPROC_PARAMS_LEN * ZBX_MAX_BYTES_IN_UTF8_CHAR + 1], *param2; const char* regexp_err = NULL; zbx_uint64_t value_ui64; + zbx_jsonpath_t jsonpath; *err = '\0'; @@ -1354,7 +1355,6 @@ static int lld_items_preproc_step_validate(const zbx_lld_item_preproc_t * pp, zb || (SUCCEED == zbx_token_find(pp->params, 0, &token, ZBX_TOKEN_SEARCH_BASIC) && 0 != (token.type & ZBX_TOKEN_USER_MACRO))) { - return SUCCEED; } @@ -1381,7 +1381,10 @@ static int lld_items_preproc_step_validate(const zbx_lld_item_preproc_t * pp, zb case ZBX_PREPROC_JSONPATH: /* break; is not missing here */ case ZBX_PREPROC_ERROR_FIELD_JSON: - ret = zbx_json_path_check(pp->params, err, sizeof(err)); + if (FAIL == (ret = zbx_jsonpath_compile(pp->params, &jsonpath))) + zbx_strlcpy(err, zbx_json_strerror(), sizeof(err)); + else + zbx_jsonpath_clear(&jsonpath); break; case ZBX_PREPROC_XPATH: /* break; is not missing here */ diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c index bae5fd0db44..83fb3a92170 100644 --- a/src/zabbix_server/preprocessor/item_preproc.c +++ b/src/zabbix_server/preprocessor/item_preproc.c @@ -171,7 +171,7 @@ int zbx_item_preproc_convert_value_to_numeric(zbx_variant_t *value_num, const zb { case ZBX_VARIANT_DBL: case ZBX_VARIANT_UI64: - zbx_variant_set_variant(value_num, value); + zbx_variant_copy(value_num, value); ret = SUCCEED; break; case ZBX_VARIANT_STR: @@ -390,7 +390,7 @@ static int item_preproc_delta(unsigned char value_type, zbx_variant_t *value, co if (ZBX_VARIANT_NONE != history_value->type) { - zbx_variant_set_variant(value, &value_num); + zbx_variant_copy(value, &value_num); if (ZBX_VARIANT_DBL == value->type || ZBX_VARIANT_DBL == history_value->type) { @@ -411,7 +411,7 @@ static int item_preproc_delta(unsigned char value_type, zbx_variant_t *value, co *history_ts = *ts; zbx_variant_clear(history_value); - zbx_variant_set_variant(history_value, &value_num); + zbx_variant_copy(history_value, &value_num); zbx_variant_clear(&value_num); return SUCCEED; @@ -911,20 +911,24 @@ static int item_preproc_regsub(zbx_variant_t *value, const char *params, char ** ******************************************************************************/ static int item_preproc_jsonpath_op(zbx_variant_t *value, const char *params, char **errmsg) { - struct zbx_json_parse jp, jp_out; + struct zbx_json_parse jp; char *data = NULL; - size_t data_alloc = 0; if (FAIL == item_preproc_convert_value(value, ZBX_VARIANT_STR, errmsg)) return FAIL; - if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_json_path_open(&jp, params, &jp_out)) + if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_jsonpath_query(&jp, params, &data)) { *errmsg = zbx_strdup(*errmsg, zbx_json_strerror()); return FAIL; } - zbx_json_value_dyn(&jp_out, &data, &data_alloc); + if (NULL == data) + { + *errmsg = zbx_strdup(*errmsg, "no data matches the specified path"); + return FAIL; + } + zbx_variant_clear(value); zbx_variant_set_str(value, data); @@ -1197,7 +1201,7 @@ static int item_preproc_validate_regex(const zbx_variant_t *value, const char *p const char *errptr = NULL; char *errmsg; - zbx_variant_set_variant(&value_str, value); + zbx_variant_copy(&value_str, value); if (FAIL == zbx_variant_convert(&value_str, ZBX_VARIANT_STR)) { @@ -1253,7 +1257,7 @@ static int item_preproc_validate_not_regex(const zbx_variant_t *value, const cha const char *errptr = NULL; char *errmsg; - zbx_variant_set_variant(&value_str, value); + zbx_variant_copy(&value_str, value); if (FAIL == zbx_variant_convert(&value_str, ZBX_VARIANT_STR)) { @@ -1311,18 +1315,10 @@ out: static int item_preproc_get_error_from_json(const zbx_variant_t *value, const char *params, char **error) { zbx_variant_t value_str; - char err[MAX_STRING_LEN]; int ret; - struct zbx_json_parse jp, jp_out; - size_t data_alloc = 0; - - if (FAIL == zbx_json_path_check(params, err, sizeof(err))) - { - *error = zbx_strdup(*error, err); - return FAIL; - } + struct zbx_json_parse jp; - zbx_variant_set_variant(&value_str, value); + zbx_variant_copy(&value_str, value); if (FAIL == (ret = item_preproc_convert_value(&value_str, ZBX_VARIANT_STR, error))) { @@ -1330,14 +1326,21 @@ static int item_preproc_get_error_from_json(const zbx_variant_t *value, const ch goto out; } - if (FAIL == zbx_json_open(value->data.str, &jp) || FAIL == zbx_json_path_open(&jp, params, &jp_out)) + if (FAIL == zbx_json_open(value->data.str, &jp)) goto out; - zbx_json_value_dyn(&jp_out, error, &data_alloc); + if (FAIL == (ret = zbx_jsonpath_query(&jp, params, error))) + { + *error = zbx_strdup(NULL, zbx_json_strerror()); + goto out; + } - zbx_lrtrim(*error, " \t\n\r"); - if ('\0' == **error) - zbx_free(*error); + if (NULL != *error) + { + zbx_lrtrim(*error, " \t\n\r"); + if ('\0' == **error) + zbx_free(*error); + } out: zbx_variant_clear(&value_str); @@ -1380,7 +1383,7 @@ static int item_preproc_get_error_from_xml(const zbx_variant_t *value, const cha xmlErrorPtr pErr; xmlBufferPtr xmlBufferLocal; - zbx_variant_set_variant(&value_str, value); + zbx_variant_copy(&value_str, value); if (FAIL == (ret = item_preproc_convert_value(&value_str, ZBX_VARIANT_STR, error))) { @@ -1473,7 +1476,7 @@ static int item_preproc_get_error_from_regex(const zbx_variant_t *value, const c int ret; char pattern[ITEM_PREPROC_PARAMS_LEN * ZBX_MAX_BYTES_IN_UTF8_CHAR + 1], *output; - zbx_variant_set_variant(&value_str, value); + zbx_variant_copy(&value_str, value); if (FAIL == (ret = item_preproc_convert_value(&value_str, ZBX_VARIANT_STR, error))) { @@ -1534,7 +1537,7 @@ static int item_preproc_throttle_value(zbx_variant_t *value, const zbx_timespec_ ret = zbx_variant_compare(value, history_value); zbx_variant_clear(history_value); - zbx_variant_set_variant(history_value, value); + zbx_variant_copy(history_value, value); if (0 == ret) zbx_variant_clear(value); @@ -1576,7 +1579,7 @@ static int item_preproc_throttle_timed_value(zbx_variant_t *value, const zbx_tim ret = zbx_variant_compare(value, history_value); zbx_variant_clear(history_value); - zbx_variant_set_variant(history_value, value); + zbx_variant_copy(history_value, value); if (ZBX_VARIANT_NONE != history_value->type) period = ts->sec - history_ts->sec; @@ -1931,7 +1934,7 @@ int zbx_item_preproc_test(unsigned char value_type, zbx_variant_t *value, const break; } - zbx_variant_set_variant(&results[i].value, value); + zbx_variant_copy(&results[i].value, value); if (ZBX_VARIANT_NONE != history_value.type) { diff --git a/src/zabbix_server/preprocessor/preproc_worker.c b/src/zabbix_server/preprocessor/preproc_worker.c index 2d09ba95414..a451b5b46b6 100644 --- a/src/zabbix_server/preprocessor/preproc_worker.c +++ b/src/zabbix_server/preprocessor/preproc_worker.c @@ -241,7 +241,7 @@ static int worker_item_preproc_execute(unsigned char value_type, zbx_variant_t * /* result history is kept to report results of steps before failing step, */ /* which means it can be omitted for the last step. */ if (i != steps_num - 1) - zbx_variant_set_variant(&results[i].value, value); + zbx_variant_copy(&results[i].value, value); else zbx_variant_set_none(&results[i].value); } @@ -302,7 +302,7 @@ static void worker_preprocess_value(zbx_ipc_socket_t *socket, zbx_ipc_message_t zbx_preprocessor_unpack_task(&itemid, &value_type, &ts, &value, &history_in, &steps, &steps_num, message->data); - zbx_variant_set_variant(&value_start, &value); + zbx_variant_copy(&value_start, &value); results = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t) * steps_num); memset(results, 0, sizeof(zbx_preproc_result_t) * steps_num); @@ -385,7 +385,7 @@ static void worker_test_value(zbx_ipc_socket_t *socket, zbx_ipc_message_t *messa message->data); zbx_variant_set_str(&value, value_str); - zbx_variant_set_variant(&value_start, &value); + zbx_variant_copy(&value_start, &value); results = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t) * steps_num); memset(results, 0, sizeof(zbx_preproc_result_t) * steps_num); diff --git a/tests/Makefile.am b/tests/Makefile.am index 2ed13192bc8..4d033c9ed96 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,4 +26,6 @@ libzbxmockdata_a_SOURCES = \ zbxmocktime.c \ zbxmockhelper.c \ zbxmockhelper.h \ - zbxmocklog.c + zbxmocklog.c \ + zbxmockjson.c \ + zbxmockjson.h diff --git a/tests/libs/zbxcommon/Makefile.am b/tests/libs/zbxcommon/Makefile.am index 55164349e3c..82c1d900edb 100644 --- a/tests/libs/zbxcommon/Makefile.am +++ b/tests/libs/zbxcommon/Makefile.am @@ -65,11 +65,11 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ - $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \ $(top_srcdir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ diff --git a/tests/libs/zbxcommon/zbx_variant_compare.c b/tests/libs/zbxcommon/zbx_variant_compare.c index 4b51fc164e3..75cf7b10f64 100644 --- a/tests/libs/zbxcommon/zbx_variant_compare.c +++ b/tests/libs/zbxcommon/zbx_variant_compare.c @@ -67,7 +67,6 @@ static void mock_read_variant(const char *path, zbx_variant_t *variant) return; } - if (0 == strcmp(type, "ZBX_VARIANT_UI64")) { zbx_uint64_t value_ui64; @@ -102,7 +101,6 @@ static void mock_read_variant(const char *path, zbx_variant_t *variant) fail_msg("Invalid variant type: %s", type); } - void zbx_mock_test_entry(void **state) { zbx_variant_t value1, value2; diff --git a/tests/libs/zbxcommon/zbx_variant_compare.yaml b/tests/libs/zbxcommon/zbx_variant_compare.yaml index b7d4f6e3c60..98e95c3eded 100644 --- a/tests/libs/zbxcommon/zbx_variant_compare.yaml +++ b/tests/libs/zbxcommon/zbx_variant_compare.yaml @@ -414,5 +414,27 @@ in: value: 12 34 56 78 90 out: return: less +--- +test case: none < uint64 +in: + value1: + type: ZBX_VARIANT_NONE + value: + value2: + type: ZBX_VARIANT_UI64 + value: 123 +out: + return: less +--- +test case: uint64 > none +in: + value1: + type: ZBX_VARIANT_UI64 + value: 123 + value2: + type: ZBX_VARIANT_NONE + value: +out: + return: greater ... diff --git a/tests/libs/zbxhistory/Makefile.am b/tests/libs/zbxhistory/Makefile.am index 74d383f60f8..503b2b0f39d 100644 --- a/tests/libs/zbxhistory/Makefile.am +++ b/tests/libs/zbxhistory/Makefile.am @@ -18,6 +18,8 @@ HISTORY_LIBS = \ $(top_srcdir)/src/libs/zbxmemory/libzbxmemory.a \ $(top_srcdir)/src/libs/zbxhistory/libzbxhistory.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ + $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxdbhigh/libzbxdbhigh.a \ $(top_srcdir)/src/libs/zbxdb/libzbxdb.a \ $(top_srcdir)/tests/libzbxmockdata.a diff --git a/tests/libs/zbxjson/Makefile.am b/tests/libs/zbxjson/Makefile.am index e1d549a2e5b..f8b706d3d92 100644 --- a/tests/libs/zbxjson/Makefile.am +++ b/tests/libs/zbxjson/Makefile.am @@ -1,14 +1,16 @@ noinst_PROGRAMS = \ - jsonpath_next \ - zbx_json_path_open \ + zbx_json_open_path \ zbx_json_decodevalue \ - zbx_json_decodevalue_dyn + zbx_json_decodevalue_dyn \ + zbx_jsonpath_compile \ + zbx_jsonpath_query JSON_LIBS = \ $(top_srcdir)/tests/libzbxmocktest.a \ $(top_srcdir)/tests/libzbxmockdata.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ @@ -21,35 +23,18 @@ JSON_LIBS = \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/tests/libzbxmockdata.a -# jsonpath_next - -jsonpath_next_SOURCES = \ - jsonpath_next.c \ - ../../zbxmocktest.h - -jsonpath_next_LDADD = $(JSON_LIBS) - -if SERVER -jsonpath_next_LDADD += @SERVER_LIBS@ -jsonpath_next_LDFLAGS = @SERVER_LDFLAGS@ -endif - -jsonpath_next_CFLAGS = -I@top_srcdir@/tests - -# zbx_json_path_open - -zbx_json_path_open_SOURCES = \ - zbx_json_path_open.c \ +zbx_json_open_path_SOURCES = \ + zbx_json_open_path.c \ ../../zbxmocktest.h -zbx_json_path_open_LDADD = $(JSON_LIBS) +zbx_json_open_path_LDADD = $(JSON_LIBS) if SERVER -zbx_json_path_open_LDADD += @SERVER_LIBS@ -zbx_json_path_open_LDFLAGS = @SERVER_LDFLAGS@ +zbx_json_open_path_LDADD += @SERVER_LIBS@ +zbx_json_open_path_LDFLAGS = @SERVER_LDFLAGS@ endif -zbx_json_path_open_CFLAGS = -I@top_srcdir@/tests +zbx_json_open_path_CFLAGS = -I@top_srcdir@/tests # zbx_json_decodevalue @@ -83,3 +68,30 @@ endif zbx_json_decodevalue_dyn_CFLAGS = -I@top_srcdir@/tests +zbx_jsonpath_compile_SOURCES = \ + zbx_jsonpath_compile.c \ + ../../zbxmocktest.h + +zbx_jsonpath_compile_LDADD = $(JSON_LIBS) + +if SERVER +zbx_jsonpath_compile_LDADD += @SERVER_LIBS@ +zbx_jsonpath_compile_LDFLAGS = @SERVER_LDFLAGS@ +endif + +zbx_jsonpath_compile_CFLAGS = -I@top_srcdir@/tests + +# zbx_jsonpath_query + +zbx_jsonpath_query_SOURCES = \ + zbx_jsonpath_query.c \ + ../../zbxmocktest.h + +zbx_jsonpath_query_LDADD = $(JSON_LIBS) + +if SERVER +zbx_jsonpath_query_LDADD += @SERVER_LIBS@ +zbx_jsonpath_query_LDFLAGS = @SERVER_LDFLAGS@ +endif + +zbx_jsonpath_query_CFLAGS = -I@top_srcdir@/tests diff --git a/tests/libs/zbxjson/jsonpath_next.c b/tests/libs/zbxjson/jsonpath_next.c deleted file mode 100644 index 435b9f3fbf0..00000000000 --- a/tests/libs/zbxjson/jsonpath_next.c +++ /dev/null @@ -1,97 +0,0 @@ -/* -** Zabbix -** Copyright (C) 2001-2019 Zabbix SIA -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -**/ - -#include "zbxmocktest.h" -#include "zbxmockdata.h" -#include "zbxmockassert.h" -#include "zbxmockutil.h" - -#include "common.h" -#include "zbxjson.h" -#include "jsonpath_next_test.h" - -void zbx_mock_test_entry(void **state) -{ - zbx_mock_handle_t components, component; - const char *path, *next = NULL, *component_class, *component_value, *result; - zbx_mock_error_t err; - zbx_strloc_t loc; - int type, ret; - char *buffer; - - ZBX_UNUSED(state); - - path = zbx_mock_get_parameter_string("in.path"); - result = zbx_mock_get_parameter_string("out.result"); - components = zbx_mock_get_parameter_handle("out.components"); - - buffer = zbx_malloc(NULL, strlen(path) + 1); - - while (1) - { - if (SUCCEED != (ret = zbx_jsonpath_next(path, &next, &loc, &type))) - { - zbx_mock_assert_str_eq("Return value", result, "fail"); - break; - } - - if (ZBX_MOCK_SUCCESS != (err = zbx_mock_vector_element(components, &component))) - { - if (ZBX_MOCK_END_OF_VECTOR == err || ZBX_MOCK_NOT_A_VECTOR == err) - fail_msg("Too many path components parsed"); - else - fail_msg("Cannot get vector element: %s", zbx_mock_error_string(err)); - } - - component_class = zbx_mock_get_object_member_string(component, "class"); - - switch (type) - { - case 0: /* ZBX_JSONPATH_COMPONENT_DOT */ - zbx_mock_assert_str_eq("Component class", component_class, "dot"); - break; - case 1: /* ZBX_JSONPATH_COMPONENT_BRACKET */ - zbx_mock_assert_str_eq("Component class", component_class, "bracket"); - break; - case 2: /* ZBX_JSONPATH_ARRAY_INDEX */ - zbx_mock_assert_str_eq("Component class", component_class, "index"); - break; - } - - zbx_strlcpy(buffer, path + loc.l, loc.r - loc.l + 2); - - component_value = zbx_mock_get_object_member_string(component, "value"); - zbx_mock_assert_str_eq("Component value", component_value, buffer); - - if ('\0' == *next) - { - zbx_mock_assert_str_eq("Return value", result, "succeed"); - break; - } - } - - if (ZBX_MOCK_SUCCESS == (err = zbx_mock_vector_element(components, &component))) - fail_msg("Too many path components parsed"); - - if (ZBX_MOCK_END_OF_VECTOR != err && ZBX_MOCK_NOT_A_VECTOR != err) - fail_msg("Cannot get vector element: %s", zbx_mock_error_string(err)); - - zbx_free(buffer); -} - diff --git a/tests/libs/zbxjson/jsonpath_next.yaml b/tests/libs/zbxjson/jsonpath_next.yaml deleted file mode 100644 index 1a8e1b3d7d2..00000000000 --- a/tests/libs/zbxjson/jsonpath_next.yaml +++ /dev/null @@ -1,265 +0,0 @@ -# Invalid JSON paths - ---- -test case: Invalid path "" - empty path -in: - path: "" -out: - result: fail - components: ---- -test case: Invalid path "$" - only root node specified -in: - path: $ -out: - result: fail - components: ---- -test case: Invalid path "$." - missing dot notation member name -in: - path: $. -out: - result: fail - components: ---- -test case: Invalid path "$['a'" - unclosed bracket notation bracket -in: - path: $['a' -out: - result: fail - components: ---- -test case: Invalid path "$[a']" - unquoted bracket notation member name -in: - path: $[a'] -out: - result: fail - components: ---- -test case: Invalid path "$['']" - empty bracked notation member name -in: - path: $[''] -out: - result: fail - components: ---- -test case: Invalid path "$.a[]" - empty bracked notation member name -in: - path: $.a[] -out: - result: fail - components: - - class: dot - value: a ---- -test case: Invalid path "$.a[1" - missing array index closing bracket -in: - path: $.a[1 -out: - result: fail - components: - - class: dot - value: a ---- -test case: Invalid path "$['a'][]" - empty bracked notation member name -in: - path: $['a'][] -out: - result: fail - components: - - class: bracket - value: a ---- -test case: Invalid path "$['a'][1" - missing array index closing bracket -in: - path: $['a'][1 -out: - result: fail - components: - - class: bracket - value: a ---- -test case: Invalid path "$.a." - missing dot notation member name -in: - path: $.a. -out: - result: fail - components: - - class: dot - value: a ---- -test case: Invalid path "$.['a']" - missing dot notation member name -in: - path: $.['a'] -out: - result: fail - components: - -# Valid JSON paths - ---- -test case: Valid path "$.a" - get dot notation first level member -in: - path: $.a -out: - result: succeed - components: - - class: dot - value: a ---- -test case: Valid path "$['a']" - get bracket notation (single quotes) first level member -in: - path: $['a'] -out: - result: succeed - components: - - class: bracket - value: a ---- -test case: Valid path "$[ 'a' ]" - get bracket notation first level member with spacing around member name -in: - path: $[ 'a' ] -out: - result: succeed - components: - - class: bracket - value: a ---- -test case: Valid path '$["a"]' - get bracket notation (double quotes) first level member -in: - path: $["a"] -out: - result: succeed - components: - - class: bracket - value: a ---- -test case: Valid path "$.a.b" - get dot notation second level member -in: - path: $.a.b -out: - result: succeed - components: - - class: dot - value: a - - class: dot - value: b ---- -test case: Valid path "$['a'].b" - get mixed notation second level member -in: - path: $['a'].b -out: - result: succeed - components: - - class: bracket - value: a - - class: dot - value: b ---- -test case: Valid path "$['a']['b']" - get bracket notation second level member -in: - path: $['a']['b'] -out: - result: succeed - components: - - class: bracket - value: a - - class: bracket - value: b ---- -test case: Valid path $.a['b'] - get mixed notation second level member -in: - path: $.a['b'] -out: - result: succeed - components: - - class: dot - value: a - - class: bracket - value: b ---- -test case: Valid path $.a[0] - get first array element -in: - path: $.a[0] -out: - result: succeed - components: - - class: dot - value: a - - class: index - value: 0 ---- -test case: Valid path $.a[0].b[1] - get array elements from mixed dot notation members and arrays -in: - path: $.a[0].b[1] -out: - result: succeed - components: - - class: dot - value: a - - class: index - value: 0 - - class: dot - value: b - - class: index - value: 1 ---- -test case: Valid path $.a[1000] - get 1001th array element -in: - path: $.a[1000] -out: - result: succeed - components: - - class: dot - value: a - - class: index - value: 1000 ---- -test case: Valid path $.a[ 1 ] - get second array element with whitespace enclosing index -in: - path: $.a[ 1 ] -out: - result: succeed - components: - - class: dot - value: a - - class: index - value: 1 ---- -test case: Valid path $['a'][2] - get third array element of bracket notation first level member -in: - path: $['a'][2] -out: - result: succeed - components: - - class: bracket - value: a - - class: index - value: 2 ---- -test case: Valid path $['a'][2]['b'][3] - get array elements from mixed bracket notation members and arrays -in: - path: $['a'][2]['b'][3] -out: - result: succeed - components: - - class: bracket - value: a - - class: index - value: 2 - - class: bracket - value: b - - class: index - value: 3 ---- -test case: Valid path $[1][2] - get nested array element -in: - path: $[1][2] -out: - result: succeed - components: - - class: index - value: 1 - - class: index - value: 2 -... diff --git a/tests/libs/zbxjson/jsonpath_next_test.c b/tests/libs/zbxjson/jsonpath_next_test.c deleted file mode 100644 index dfe70beb8d4..00000000000 --- a/tests/libs/zbxjson/jsonpath_next_test.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -** Zabbix -** Copyright (C) 2001-2019 Zabbix SIA -** -** This program is free software; you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation; either version 2 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software -** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -**/ - -#include "jsonpath_next_test.h" - -int zbx_jsonpath_next(const char *path, const char **pnext, zbx_strloc_t *loc, int *type) -{ - return jsonpath_next(path, pnext, loc, type); -} diff --git a/tests/libs/zbxjson/mock_json.c b/tests/libs/zbxjson/mock_json.c index 56172b0b3ac..7bd2454a2d6 100644 --- a/tests/libs/zbxjson/mock_json.c +++ b/tests/libs/zbxjson/mock_json.c @@ -23,6 +23,7 @@ #include "common.h" #include "zbxjson.h" +#include "mock_json.h" const char *zbx_mock_json_type_to_str(int type) { diff --git a/tests/libs/zbxjson/zbx_json_path_open.c b/tests/libs/zbxjson/zbx_json_open_path.c index e65b491e819..6ef6e25cbf6 100644 --- a/tests/libs/zbxjson/zbx_json_path_open.c +++ b/tests/libs/zbxjson/zbx_json_open_path.c @@ -24,6 +24,21 @@ #include "common.h" #include "zbxjson.h" +#include "../../../src/libs/zbxjson/json.h" + +static void json_value_dyn(const struct zbx_json_parse *jp, char **string, size_t *string_alloc) +{ + if (NULL == zbx_json_decodevalue_dyn(jp->start, string, string_alloc, NULL)) + { + size_t len = jp->end - jp->start + 2; + + if (*string_alloc < len) + *string = (char *)zbx_realloc(*string, len); + + zbx_strlcpy(*string, jp->start, len); + } +} + void zbx_mock_test_entry(void **state) { @@ -42,8 +57,9 @@ void zbx_mock_test_entry(void **state) ret = zbx_json_open(json, &jp); zbx_mock_assert_result_eq("Invalid zbx_json_open() return value", SUCCEED, ret); - if (FAIL == (ret = zbx_json_path_open(&jp, path, &jp_out))) + if (FAIL == (ret = zbx_json_open_path(&jp, path, &jp_out))) { + printf("zbx_json_path_open() error: %s\n", zbx_json_strerror()); zbx_mock_assert_str_eq("Invalid zbx_json_path_open() return value", result, "fail"); return; } @@ -51,7 +67,7 @@ void zbx_mock_test_entry(void **state) zbx_mock_assert_result_eq("Invalid zbx_json_path_open() return value", SUCCEED, ret); zbx_mock_assert_str_eq("Invalid zbx_json_path_open() return value", result, "succeed"); - zbx_json_value_dyn(&jp_out, &buffer, &size); + json_value_dyn(&jp_out, &buffer, &size); value = zbx_mock_get_parameter_string("out.value"); zbx_mock_assert_str_eq("Invalid value", value, buffer); diff --git a/tests/libs/zbxjson/zbx_json_path_open.yaml b/tests/libs/zbxjson/zbx_json_open_path.yaml index 5489806ee58..ea868f5490a 100644 --- a/tests/libs/zbxjson/zbx_json_path_open.yaml +++ b/tests/libs/zbxjson/zbx_json_open_path.yaml @@ -369,4 +369,46 @@ in: out: result: succeed value: "𐀀𐀀𐀀𐀀" +--- +test case: 'Invalid path $.*.b in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $.*.b +out: + result: fail +--- +test case: 'Invalid path $.a.b[?(@.x == "10")] in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $.a.b[?(@.x == "10")] +out: + result: fail +--- +test case: 'Invalid path $["a", "x"].b in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $['a', 'x'].b +out: + result: fail +--- +test case: 'Invalid path $.a.b[1,2] in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $.a.b[1,2] +out: + result: fail +--- +test case: 'Invalid path $.a.b.length() in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $.a.b.length() +out: + result: fail +--- +test case: 'Invalid path $..b in {"a":{"b": [{"x":10}, 2, 3] }}' +in: + json: '{"a":{"b": [{"x":10}, 2, 3] }}' + path: $..b +out: + result: fail ... diff --git a/tests/libs/zbxjson/zbx_jsonpath_compile.c b/tests/libs/zbxjson/zbx_jsonpath_compile.c new file mode 100644 index 00000000000..6cfe255d2aa --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonpath_compile.c @@ -0,0 +1,257 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockassert.h" +#include "zbxmockutil.h" + +#include "common.h" +#include "zbxjson.h" + +#include "../../../src/libs/zbxjson/jsonpath.h" +#include "../../../src/libs/zbxjson/json.h" + +static int mock_str_to_segment_type(const char *segment_type) +{ + if (0 == strcmp("ZBX_JSONPATH_SEGMENT_MATCH_ALL", segment_type)) + return ZBX_JSONPATH_SEGMENT_MATCH_ALL; + if (0 == strcmp("ZBX_JSONPATH_SEGMENT_MATCH_LIST", segment_type)) + return ZBX_JSONPATH_SEGMENT_MATCH_LIST; + if (0 == strcmp("ZBX_JSONPATH_SEGMENT_MATCH_SLICE", segment_type)) + return ZBX_JSONPATH_SEGMENT_MATCH_RANGE; + if (0 == strcmp("ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION", segment_type)) + return ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION; + if (0 == strcmp("ZBX_JSONPATH_SEGMENT_FUNCTION", segment_type)) + return ZBX_JSONPATH_SEGMENT_FUNCTION; + + fail_msg("Unknown jsonpath segment type: %s", segment_type); + return -1; +} + +static void jsonpath_token_print(char **data, size_t *data_alloc, size_t *data_offset, + const zbx_jsonpath_token_t *token) +{ + switch (token->type) + { + case ZBX_JSONPATH_TOKEN_PATH_ABSOLUTE: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_PATH_RELATIVE: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_CONST_STR: + ZBX_FALLTHROUGH; + case ZBX_JSONPATH_TOKEN_CONST_NUM: + zbx_strcpy_alloc(data, data_alloc, data_offset, token->data); + break; + case ZBX_JSONPATH_TOKEN_PAREN_LEFT: + zbx_strcpy_alloc(data, data_alloc, data_offset, "("); + break; + case ZBX_JSONPATH_TOKEN_PAREN_RIGHT: + zbx_strcpy_alloc(data, data_alloc, data_offset, ")"); + break; + case ZBX_JSONPATH_TOKEN_OP_PLUS: + zbx_strcpy_alloc(data, data_alloc, data_offset, "+"); + break; + case ZBX_JSONPATH_TOKEN_OP_MINUS: + zbx_strcpy_alloc(data, data_alloc, data_offset, "-"); + break; + case ZBX_JSONPATH_TOKEN_OP_MULT: + zbx_strcpy_alloc(data, data_alloc, data_offset, "*"); + break; + case ZBX_JSONPATH_TOKEN_OP_DIV: + zbx_strcpy_alloc(data, data_alloc, data_offset, "/"); + break; + case ZBX_JSONPATH_TOKEN_OP_EQ: + zbx_strcpy_alloc(data, data_alloc, data_offset, "=="); + break; + case ZBX_JSONPATH_TOKEN_OP_NE: + zbx_strcpy_alloc(data, data_alloc, data_offset, "!="); + break; + case ZBX_JSONPATH_TOKEN_OP_GT: + zbx_strcpy_alloc(data, data_alloc, data_offset, ">"); + break; + case ZBX_JSONPATH_TOKEN_OP_GE: + zbx_strcpy_alloc(data, data_alloc, data_offset, ">="); + break; + case ZBX_JSONPATH_TOKEN_OP_LT: + zbx_strcpy_alloc(data, data_alloc, data_offset, "<"); + break; + case ZBX_JSONPATH_TOKEN_OP_LE: + zbx_strcpy_alloc(data, data_alloc, data_offset, "<="); + break; + case ZBX_JSONPATH_TOKEN_OP_NOT: + zbx_strcpy_alloc(data, data_alloc, data_offset, "!"); + break; + case ZBX_JSONPATH_TOKEN_OP_AND: + zbx_strcpy_alloc(data, data_alloc, data_offset, "&&"); + break; + case ZBX_JSONPATH_TOKEN_OP_OR: + zbx_strcpy_alloc(data, data_alloc, data_offset, "||"); + break; + case ZBX_JSONPATH_TOKEN_OP_REGEXP: + zbx_strcpy_alloc(data, data_alloc, data_offset, "=~"); + break; + default: + zbx_strcpy_alloc(data, data_alloc, data_offset, "?"); + break; + } +} + +static char *segment_data_to_str(const zbx_jsonpath_segment_t *segment) +{ + const char *functions[] = {"unknown", "min()", "max()", "avg()", "length()", "first()"}; + char *data = NULL; + size_t data_alloc = 0, data_offset = 0; + int i; + zbx_jsonpath_list_node_t *node; + zbx_vector_ptr_t nodes; + + switch (segment->type) + { + case ZBX_JSONPATH_SEGMENT_MATCH_ALL: + data = zbx_strdup(NULL, "*"); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_LIST: + zbx_vector_ptr_create(&nodes); + + /* lists are kept in reverse order, invert it for clarity */ + for (node = segment->data.list.values; NULL != node; node = node->next) + zbx_vector_ptr_append(&nodes, node); + + for (i = nodes.values_num - 1; i >= 0; i--) + { + node = (zbx_jsonpath_list_node_t *)nodes.values[i]; + + if (ZBX_JSONPATH_LIST_NAME == segment->data.list.type) + { + zbx_snprintf_alloc(&data, &data_alloc, &data_offset, "\'%s'", + (char *)&node->data); + } + else + { + zbx_snprintf_alloc(&data, &data_alloc, &data_offset, "%d", + *(int *)&node->data); + } + + if (0 != i) + zbx_chrcpy_alloc(&data, &data_alloc, &data_offset, ','); + } + + zbx_vector_ptr_destroy(&nodes); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_RANGE: + if (0 != (segment->data.range.flags & 0x01)) + zbx_snprintf_alloc(&data, &data_alloc, &data_offset, "%d", segment->data.range.start); + zbx_chrcpy_alloc(&data, &data_alloc, &data_offset, ':'); + if (0 != (segment->data.range.flags & 0x02)) + zbx_snprintf_alloc(&data, &data_alloc, &data_offset, "%d", segment->data.range.end); + break; + case ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION: + for (i = 0; i < segment->data.expression.tokens.values_num; i++) + { + if (0 != i) + zbx_strcpy_alloc(&data, &data_alloc, &data_offset, " , "); + + jsonpath_token_print(&data, &data_alloc, &data_offset, + segment->data.expression.tokens.values[i]); + } + break; + case ZBX_JSONPATH_SEGMENT_FUNCTION: + zbx_strcpy_alloc(&data, &data_alloc, &data_offset, functions[segment->data.function.type]); + break; + default: + data = zbx_strdup(NULL, "unknown"); + break; + } + + return data; +} + +static void validate_segment(int index, const char *segment_type, const char *segment_data, int detached, + const zbx_jsonpath_segment_t *segment) +{ + int type; + char prefix[MAX_STRING_LEN]; + char *data = NULL; + + zbx_snprintf(prefix, sizeof(prefix), "jsonpath segment #%d type", index + 1); + type = mock_str_to_segment_type(segment_type); + zbx_mock_assert_int_eq(prefix, type, segment->type); + + zbx_snprintf(prefix, sizeof(prefix), "jsonpath segment #%d detached", index + 1); + zbx_mock_assert_int_eq(prefix, detached, segment->detached); + + data = segment_data_to_str(segment); + zbx_snprintf(prefix, sizeof(prefix), "jsonpath segment #%d data", index + 1); + zbx_mock_assert_str_eq(prefix, segment_data, data); + zbx_free(data); +} + +void zbx_mock_test_entry(void **state) +{ + zbx_jsonpath_t jsonpath; + int returned_ret, expected_ret, index = 0; + zbx_mock_handle_t hsegments, hsegment, hdetached; + const char *segment_data, *segment_type, *value; + + ZBX_UNUSED(state); + + /* reset json error to check if compilation will set it */ + zbx_set_json_strerror("%s", ""); + + returned_ret = zbx_jsonpath_compile(zbx_mock_get_parameter_string("in.path"), &jsonpath); + + if (FAIL == returned_ret) + printf("zbx_jsonpath_compile() error: %s\n", zbx_json_strerror()); + + expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.result")); + zbx_mock_assert_result_eq("zbx_jsonpath_compile() return value", expected_ret, returned_ret); + + if (SUCCEED == returned_ret) + { + zbx_mock_assert_uint64_eq("jsonpath definite flag", zbx_mock_get_parameter_uint64("out.definite"), + jsonpath.definite); + hsegments = zbx_mock_get_parameter_handle("out.segments"); + + while (ZBX_MOCK_SUCCESS == zbx_mock_vector_element(hsegments, &hsegment)) + { + int detached = 0; + + zbx_mock_assert_int_ne("Too many path segments parsed", index, jsonpath.segments_num); + + segment_type = zbx_mock_get_object_member_string(hsegment, "type"); + segment_data = zbx_mock_get_object_member_string(hsegment, "data"); + + if (ZBX_MOCK_SUCCESS == zbx_mock_object_member(hsegment, "detached", &hdetached) && + ZBX_MOCK_SUCCESS == zbx_mock_string(hdetached, &value)) + { + detached = atoi(value); + } + + validate_segment(index, segment_type, segment_data, detached, &jsonpath.segments[index]); + index++; + } + + zbx_mock_assert_int_eq("Not enough path segments parsed", index, jsonpath.segments_num); + + zbx_jsonpath_clear(&jsonpath); + } + else + zbx_mock_assert_str_ne("zbx_jsonpath_compile() error", "", zbx_json_strerror()); +} diff --git a/tests/libs/zbxjson/zbx_jsonpath_compile.yaml b/tests/libs/zbxjson/zbx_jsonpath_compile.yaml new file mode 100644 index 00000000000..ec6475ee824 --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonpath_compile.yaml @@ -0,0 +1,1024 @@ +# Invalid definite jsonpaths + +--- +test case: Compile fail "" +in: + path: "" +out: + result: FAIL +--- +test case: Compile fail $ +in: + path: $ +out: + result: FAIL +--- +test case: Compile fail "$." +in: + path: $. +out: + result: FAIL +--- +test case: Compile fail "$['a'" +in: + path: $['a' +out: + result: FAIL +--- +test case: Compile fail "$[a']" +in: + path: $[a'] +out: + result: FAIL +--- +test case: Compile fail "$['']" +in: + path: $[''] +out: + result: FAIL +--- +test case: Compile fail "$.a[]" +in: + path: $.a[] +out: + result: FAIL +--- +test case: Compile fail "$.a[1" +in: + path: $.a[1 +out: + result: FAIL +--- +test case: Compile fail "$['a'][]" +in: + path: $['a'][] +out: + result: FAIL +--- +test case: Compile fail "$['a'][1" +in: + path: $['a'][1 +out: + result: FAIL +--- +test case: Compile fail "$.a." +in: + path: $.a. +out: + result: FAIL +--- + +# Valid definite jsonpaths +test case: Compile success "$.a" +in: + path: $.a +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" +--- +test case: Compile success "$['a']" +in: + path: $['a'] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" +--- +test case: Compile success "$[ 'a' ]" +in: + path: $[ 'a' ] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" +--- +test case: Compile success '$["a"]' +in: + path: $["a"] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" +--- +test case: Compile success "$.a.b" +in: + path: $.a.b +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" +--- +test case: Compile success "$['a'].b" +in: + path: $['a'].b +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" +--- +test case: Compile success "$['a']['b']" +in: + path: $['a']['b'] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" +--- +test case: Compile success $.a['b'] +in: + path: $.a['b'] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" +--- +test case: Compile success $.a[0] +in: + path: $.a[0] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 0 +--- +test case: Compile success $[-1] +in: + path: $[-1] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: -1 +--- +test case: Compile success $.a[0].b[1] +in: + path: $.a[0].b[1] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 0 + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1 +--- +test case: Compile success $.a[1000] +in: + path: $.a[1000] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1000 +--- +test case: Compile success $.a[ 1 ] +in: + path: $.a[ 1 ] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1 +--- +test case: Compile success $['a'][2] +in: + path: $['a'][2] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 2 +--- +test case: Compile success $['a'][2]['b'][3] +in: + path: $['a'][2]['b'][3] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 2 + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 3 +--- +test case: Compile success $[1][2] +in: + path: $[1][2] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1 + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 2 +--- +test case: Compile success $.['a'].['b'] +in: + path: $.['a'].['b'] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" +--- +# Invalid list based json filters + +test case: Compile fail $...a +in: + path: $...a +out: + result: FAIL +--- +test case: 'Compile fail $.**.a' +in: + path: '$.**.a' +out: + result: FAIL +--- +test case: Compile fail $[,1] +in: + path: $[,1] +out: + result: FAIL +--- +test case: Compile fail $[1,] +in: + path: $[1,] +out: + result: FAIL +--- +test case: Compile fail $[1,,2] +in: + path: $[1,,2] +out: + result: FAIL +--- +test case: Compile fail $[1,*,2] +in: + path: $[1,*,2] +out: + result: FAIL +--- +test case: Compile fail $[--1] +in: + path: $[--1] +out: + result: FAIL +--- +test case: Compile fail $[a] +in: + path: $[a] +out: + result: FAIL +--- +test case: Compile fail $[$a$] +in: + path: $[$a$] +out: + result: FAIL +--- +test case: Compile fail $[,'a'] +in: + path: $[,'a'] +out: + result: FAIL +--- +test case: Compile fail $['a',] +in: + path: $['a',] +out: + result: FAIL +--- +test case: Compile fail $['a',,'b'] +in: + path: $['a',,2] +out: + result: FAIL +--- +test case: Compile fail $['a',*,'b'] +in: + path: $['a',*,2] +out: + result: FAIL +--- +test case: Compile fail $[--'a'] +in: + path: $[--'a'] +out: + result: FAIL +--- +test case: Compile fail $[1,'a'] +in: + path: $[1,'a'] +out: + result: FAIL +--- +test case: Compile fail $['a\a'] +in: + path: $['a\a'] +out: + result: FAIL +--- +test case: Compile fail $['a\'] +in: + path: $['a\'] +out: + result: FAIL +--- +test case: Compile fail $['a\"'] +in: + path: $['a\"'] +out: + result: FAIL +--- +test case: Compile fail $['a\] +in: + path: $['a\] +out: + result: FAIL +--- +test case: Compile fail $['a\ +in: + path: $['a\ +out: + result: FAIL +--- +test case: Compile fail $["a\'"] +in: + path: $["a\'"] +out: + result: FAIL +--- +# Valid list based json filters + +test case: Compile success $[ 1 ] +in: + path: $[ 1 ] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1 +--- +test case: Compile success $[1,2] +in: + path: $[1,2] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1,2 +--- +test case: Compile success $[ 1 , 2 ] +in: + path: $[ 1 , 2 ] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: 1,2 +--- +test case: Compile success $[ -1 ] +in: + path: $[ -1 ] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: -1 +--- +test case: Compile success $[ -1, 2, 3 ] +in: + path: $[ -1, 2, 3 ] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: -1,2,3 +--- +test case: Compile success $[ ' a ' ] +in: + path: $[ ' a ' ] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "' a '" +--- +test case: Compile success $[ ' a ', "b" ] +in: + path: $[ ' a ', "b" ] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "' a ','b'" +--- +test case: Compile success $['a \'b\' \\/'] +in: + path: $['a \'b\' \\/'] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a 'b' \\/'" +--- +test case: Compile success $["a \"b\""] +in: + path: $["a \"b\""] +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a \"b\"'" +--- +test case: Compile success $.*.a +in: + path: $.*.a +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_ALL + data: "*" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" +--- +test case: Compile success $.* +in: + path: $.* +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_ALL + data: "*" +--- +test case: Compile success $[*] +in: + path: $[*] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_ALL + data: "*" +--- +test case: Compile success $..a.b..c +in: + path: $..a.b..c +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'a'" + detached: 1 + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'b'" + - type: ZBX_JSONPATH_SEGMENT_MATCH_LIST + data: "'c'" + detached: 1 +--- +# Slice based json filters + +test case: Compile fail $[1:2:3] +in: + path: $[1:2:3] +out: + result: FAIL +--- +test case: Compile success $[:] +in: + path: "$[:]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: ":" +--- +test case: Compile success $[1:] +in: + path: "$[1:]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: "1:" +--- +test case: Compile success $[:1] +in: + path: "$[:1]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: ":1" +--- +test case: Compile success $[1:2] +in: + path: "$[1:2]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: "1:2" +--- +test case: "Compile success $[-1:-1]" +in: + path: "$[-1:-1]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: "-1:-1" +--- +test case: "Compile success $[ 1 : 2 ]" +in: + path: "$[ 1 : 2 ]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: "1:2" +--- +test case: "Compile success $[ : 1 ]" +in: + path: "$[ : 1 ]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: ":1" +--- +test case: "Compile success $[ 1 : ]" +in: + path: "$[ 1 : ]" +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_SLICE + data: "1:" +--- +test case: Compile fail $.min( +in: + path: $.min( +out: + result: FAIL +--- +test case: Compile fail $.min(1) +in: + path: $.min(1) +out: + result: FAIL +--- +test case: Compile fail $.div() +in: + path: $.div() +out: + result: FAIL +--- +test case: Compile fail $.avg().data +in: + path: $.avg().data +out: + result: FAIL +--- +test case: Compile fail $.len() +in: + path: $.len() +out: + result: FAIL +--- +test case: Compile success $.min() +in: + path: $.min() +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "min()" +--- +test case: Compile success $.max() +in: + path: $.max() +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "max()" +--- +test case: Compile success $.avg() +in: + path: $.avg() +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "avg()" +--- +test case: Compile success $.length() +in: + path: $.length() +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "length()" +--- +test case: Compile success $.first().max() +in: + path: $.first().max() +out: + result: SUCCEED + definite: 1 + segments: + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "first()" + - type: ZBX_JSONPATH_SEGMENT_FUNCTION + data: "max()" +--- +test case: Compile fail $[?] +in: + path: $[?] +out: + result: FAIL +--- +test case: Compile fail $[?(] +in: + path: $[?(] +out: + result: FAIL +--- +test case: Compile fail $[?()] +in: + path: $[?()] +out: + result: FAIL +--- +test case: Compile fail $[?(+)] +in: + path: $[?(+)] +out: + result: FAIL +--- +test case: Compile fail $[?(==)] +in: + path: $[?(==)] +out: + result: FAIL +--- +test case: Compile fail $[?(()] +in: + path: $[?(()] +out: + result: FAIL +--- +test case: Compile fail $[?(+1)] +in: + path: $[?(+1)] +out: + result: FAIL +--- +test case: Compile fail $[?(!)] +in: + path: $[?(!)] +out: + result: FAIL +--- +test case: Compile fail $[?(a)] +in: + path: $[?(a)] +out: + result: FAIL +--- +test case: Compile fail $[?(())] +in: + path: $[?(())] +out: + result: FAIL +--- +test case: Compile fail $[?(1++1)] +in: + path: $[?(1++1)] +out: + result: FAIL +--- +test case: Compile fail $[?(1 - - 1)] +in: + path: $[?(1 - - 1)] +out: + result: FAIL +--- +test case: Compile fail $[?(1 + + 1)] +in: + path: $[?(1 + + 1)] +out: + result: FAIL +--- +test case: Compile fail $[?((1+1)+)] +in: + path: $[?((1+1)+)] +out: + result: FAIL +--- +test case: Compile fail $[?((1 + 1) + ())] +in: + path: $[?((1 + 1) + ())] +out: + result: FAIL +--- +test case: Compile fail $[?(a + 1)] +in: + path: $[?(a + 1)] +out: + result: FAIL +--- +test case: Compile fail $[?(1 + 'a)] +in: + path: $[?(1 + 'a)] +out: + result: FAIL +--- +test case: Compile fail $[?($..b + 1)] +in: + path: $[?($..b + 1)] +out: + result: FAIL +--- +test case: Compile fail $[?($['a])] +in: + path: $[?($['a])] +out: + result: FAIL +--- +test case: Compile fail $[?($[1,2])] +in: + path: $[?($[1,2])] +out: + result: FAIL +--- +test case: Compile fail $[?($.a.)] +in: + path: $[?($.a.)] +out: + result: FAIL +--- +test case: Compile success $[?(1)] +in: + path: $[?(1)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1" +--- +test case: Compile success $[?(-1)] +in: + path: $[?(-1)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "-1" +--- +test case: Compile success $[?($.a)] +in: + path: $[?($.a)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "$.a" +--- +test case: Compile success $[?(@.b)] +in: + path: $[?(@.b)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "@.b" +--- +test case: Compile success $[?(!1)] +in: + path: $[?(!1)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , !" +--- +test case: Compile success $[?(1 + 2)] +in: + path: $[?(1 + 2)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , +" +--- +test case: Compile success $[?(1 + 2 * 3)] +in: + path: $[?(1 + 2 * 3)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , 3 , * , +" +--- +test case: Compile success $[?(1 - 2 / 3)] +in: + path: $[?(1 - 2 / 3)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , 3 , / , -" +--- +test case: Compile success $[?((1 + 2) * 3)] +in: + path: $[?((1 + 2) * 3)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , + , 3 , *" +--- +test case: Compile success $[?($.a + 2 == 3)] +in: + path: $[?($.a + 2 == 3)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "$.a , 2 , + , 3 , ==" +--- +test case: Compile success $[?((1 + 2) * (3 - 4))] +in: + path: $[?((1 + 2) * (3 - 4))] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , + , 3 , 4 , - , *" +--- +test case: Compile success $[?(1 == 2 || !3*5)] +in: + path: $[?(1 == 2 || !3*5)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , == , 3 , ! , 5 , * , ||" +--- +test case: Compile success $[?(1 == 2 || !(3*5))] +in: + path: $[?(1 == 2 || !(3*5))] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , == , 3 , 5 , * , ! , ||" +--- +test case: Compile success $[?(1 == 2 * @.a)] +in: + path: $[?(1 == 2 * @.a)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , @.a , * , ==" +--- +test case: Compile success $[?((1)+(2))] +in: + path: $[?((1)+(2))] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , +" +--- +test case: Compile success $[?(1 + 2 - 3 + 4)] +in: + path: $[?(1 + 2 - 3 + 4)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , + , 3 , - , 4 , +" +--- +test case: Compile success $[?(1 == 2 == 3)] +in: + path: $[?(1 == 2 == 3)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "1 , 2 , == , 3 , ==" +--- +test case: Compile fail $[?(1 == 2 == 3] +in: + path: $[?(1 == 2 == 3] +out: + result: FAIL +--- +test case: Compile success $..[?(@.id)] +in: + path: $..[?(@.id)] +out: + result: SUCCEED + definite: 0 + segments: + - type: ZBX_JSONPATH_SEGMENT_MATCH_EXPRESSION + data: "@.id" + detached: 1 +... diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.c b/tests/libs/zbxjson/zbx_jsonpath_query.c new file mode 100644 index 00000000000..10a7db13155 --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonpath_query.c @@ -0,0 +1,100 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockassert.h" +#include "zbxmockutil.h" +#include "zbxmockjson.h" + +#include "common.h" +#include "zbxjson.h" +#include "../../../src/libs/zbxjson/json.h" + +static void check_definite_path_result(zbx_mock_handle_t handle, const char *returned_output) +{ + const char *expected_output; + struct zbx_json_parse jp; + + if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) + fail_msg("Invalid test case out.value parameter"); + + if (FAIL == zbx_json_open(expected_output, &jp)) + zbx_mock_assert_str_eq("Definite query result", expected_output, returned_output); + else + zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); +} + +static void check_indefinite_path_result(zbx_mock_handle_t handle, const char *returned_output) +{ + const char *expected_output; + + if (ZBX_MOCK_SUCCESS != zbx_mock_string(handle, &expected_output)) + fail_msg("Invalid test case out.values parameter"); + + zbx_mock_assert_json_eq("Indefinite query result", expected_output, returned_output); +} + +void zbx_mock_test_entry(void **state) +{ + const char *data, *path; + struct zbx_json_parse jp; + char *output = NULL; + int expected_ret, returned_ret; + zbx_mock_handle_t handle; + + ZBX_UNUSED(state); + + /* reset json error to check if compilation will set it */ + zbx_set_json_strerror("%s", ""); + + data = zbx_mock_get_parameter_string("in.data"); + if (FAIL == zbx_json_open(data, &jp)) + fail_msg("Invalid json data: %s", zbx_json_strerror()); + + path = zbx_mock_get_parameter_string("in.path"); + returned_ret = zbx_jsonpath_query(&jp, path, &output); + expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); + + if (FAIL == returned_ret) + printf("\tzbx_jsonpath_query() failed with: %s\n", zbx_json_strerror()); + + zbx_mock_assert_result_eq("zbx_jsonpath_query() return value", expected_ret, returned_ret); + + if (SUCCEED == returned_ret) + { + printf("\tzbx_jsonpath_query() query result: %s\n", ZBX_NULL2EMPTY_STR(output)); + if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.value", &handle)) + { + zbx_mock_assert_ptr_ne("Query result", NULL, output); + check_definite_path_result(handle, output); + } + else if (ZBX_MOCK_SUCCESS == zbx_mock_parameter("out.values", &handle)) + { + zbx_mock_assert_ptr_ne("Query result", NULL, output); + check_indefinite_path_result(handle, output); + } + else + zbx_mock_assert_ptr_eq("Query result", NULL, output); + } + else + zbx_mock_assert_str_ne("tzbx_jsonpath_query() error", "", zbx_json_strerror()); + + zbx_free(output); +} diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml b/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml new file mode 100644 index 00000000000..456f49ebe28 --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonpath_query.inc.yaml @@ -0,0 +1,77 @@ +--- +| + { + "books": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ], + "services": { + "delivery": { + "servicegroup": 1000, + "description": "Next day delivery in local town", + "active": true, + "price": 5 + }, + "bookbinding": { + "servicegroup": 1001, + "description": "Printing and assembling book in A5 format", + "active": true, + "price": 154.99 + }, + "restoration": { + "servicegroup": 1002, + "description": "Various restoration methods", + "active": false, + "methods": [ + { + "description": "Checmical cleaning", + "price": 46 + }, + { + "description": "Pressing pages damaged by moisture", + "price": 24.5 + }, + { + "description": "Rebinding torn book", + "price": 99.49 + } + ] + } + }, + "filters": { + "price": 10, + "category": "fiction", + "no filters": "no \"filters\"" + }, + "closed message": "Store is closed", + "tags": ["a", "b", "c", "d", "e" ] + } +... diff --git a/tests/libs/zbxjson/zbx_jsonpath_query.yaml b/tests/libs/zbxjson/zbx_jsonpath_query.yaml new file mode 100644 index 00000000000..25b1e8888fc --- /dev/null +++ b/tests/libs/zbxjson/zbx_jsonpath_query.yaml @@ -0,0 +1,711 @@ +--- +test case: Query $[0] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[0] +out: + return: SUCCEED + value: a +--- +test case: Query $[4] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[4] +out: + return: SUCCEED +--- +test case: Query $[1] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[1] +out: + return: SUCCEED + value: b +--- +test case: Query $[2] from ["a", "b"] +in: + data: '["a", "b"]' + path: $[2] +out: + return: SUCCEED +--- +test case: Query $[2].* from ["a", "b"] +in: + data: '["a", "b"]' + path: $[2].* +out: + return: SUCCEED +--- +test case: Query $.filters.price +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.filters.price +out: + return: SUCCEED + value: 10 +--- +test case: Query $.filters.category +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.filters.category +out: + return: SUCCEED + value: fiction +--- +test case: Query $.filters.id +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.filters.id +out: + return: SUCCEED +--- +test case: Query $.books[1].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[1].title +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $['closed message'] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $['closed message'] +out: + return: SUCCEED + value: Store is closed +--- +test case: Query $.books[-1].author +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[-1].author +out: + return: SUCCEED + value: J. R. R. Tolkien +--- +test case: Query $.filters +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.filters +out: + return: SUCCEED + value: | + { + "price": 10, + "category": "fiction", + "no filters": "no \"filters\"" + } +--- +test case: Query $.books.length() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books.length() +out: + return: SUCCEED + value: 4 +--- +test case: Query $.tags[:] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[:] +out: + return: SUCCEED + value: '["a", "b", "c", "d", "e" ]' +--- +test case: Query $.tags[2:] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[2:] +out: + return: SUCCEED + value: '["c", "d", "e" ]' +--- +test case: Query $.tags[:2] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[:2] +out: + return: SUCCEED + value: '["a", "b"]' +--- +test case: Query $.tags[1:4] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[1:4] +out: + return: SUCCEED + value: '["b", "c", "d"]' +--- +test case: Query $.tags[-2:] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[-2:] +out: + return: SUCCEED + value: '["d", "e"]' +--- +test case: Query $.tags[:-3] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[:-3] +out: + return: SUCCEED + value: '["a", "b"]' +--- +test case: Query $.tags[:-3].length() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.tags[:-3].length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.books[0, 2].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[0, 2].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick"]' +--- +test case: Query $.books[1]['author', "title"] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[1]['author', "title"] +out: + return: SUCCEED + value: '["Evelyn Waugh", "Sword of Honour"]' +--- +test case: Query $..id +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..id +out: + return: SUCCEED + value: '[1, 2, 3, 4]' +--- +test case: Query $.services..price +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.services..price +out: + return: SUCCEED + value: '[5, 154.99, 46, 24.5, 99.49]' +--- +test case: Query $.books[?(@.id == 1 + 1)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 1 + 1)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 4 / 2)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 4 / 2)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 7 - 5)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 7 - 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 0.4 * 5)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 0.4 * 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 4 - 0.4 * 5)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 4 - 0.4 * 5)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == -0.4 * 5 + 4)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == -0.4 * 5 + 4)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 0.4 * (-5) + 4)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 0.4 * (-5) + 4)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 2 || @.id == 4)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 || @.id == 4)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 && 2 * ((1 + 3) / 2 + 3) == 10)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.id == 2 == 1)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2 == 1)].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(!(@.id == 2))].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(!(@.id == 2))].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.id != 2)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id != 2)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.title =~ " of ")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.title =~ " of ")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price > 12.99)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price > 12.99)].title +out: + return: SUCCEED + value: '["The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price >= 12.99)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price >= 12.99)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.price < 12.99)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price < 12.99)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick"]' +--- +test case: Query $.books[?(@.price <= 12.99)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price <= 12.99)].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Sword of Honour", "Moby Dick"]' +--- +test case: Query $.books[?(@.author > "Herman Melville")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.author > "Herman Melville")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.author >= "Herman Melville")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.author >= "Herman Melville")].title +out: + return: SUCCEED + value: '["Sayings of the Century", "Moby Dick", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.author < "Herman Melville")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.author < "Herman Melville")].title +out: + return: SUCCEED + value: '["Sword of Honour"]' +--- +test case: Query $.books[?(@.author <= "Herman Melville")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.author <= "Herman Melville")].title +out: + return: SUCCEED + value: '["Sword of Honour", "Moby Dick"]' +--- +test case: Query $.books[?(@.price > $.filters.price)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price > $.filters.price)].title +out: + return: SUCCEED + value: '["Sword of Honour", "The Lord of the Rings"]' +--- +test case: Query $.books[?(@.category == $.filters.category)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == $.filters.category)].title +out: + return: SUCCEED + value: '["Sword of Honour","Moby Dick","The Lord of the Rings"]' +--- +test case: Query $.books[?(@.category != $.filters.category)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.category != $.filters.category)].title +out: + return: SUCCEED + value: '["Sayings of the Century"]' +--- +test case: Query $..[?(@.id)] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..[?(@.id)] +out: + return: SUCCEED + value: | + [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ] +--- +test case: Query $.services..[?(@.price > 50)].description +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.services..[?(@.price > 50)].description +out: + return: SUCCEED + value: '["Printing and assembling book in A5 format", "Rebinding torn book"]' +--- +test case: Query $..id.length() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..id.length() +out: + return: SUCCEED + value: 4 +--- +test case: Query $.books[?(@.price >= 12.99)].length() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.price >= 12.99)].length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.books[?(@.id == 2)].title.first() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.id == 2)].title.first() +out: + return: SUCCEED + value: Sword of Honour +--- +test case: Query $..tags.first().length() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..tags.first().length() +out: + return: SUCCEED + value: 5 +--- +test case: Query $.length() from ["a", "b"] +in: + data: '["a", "b"]' + path: $.length() +out: + return: SUCCEED + value: 2 +--- +test case: Query $.first() from ["a", "b"] +in: + data: '["a", "b"]' + path: $.first() +out: + return: SUCCEED + value: a +--- +test case: Query $.first().first() from [["a", "b"]] +in: + data: '[["a", "b"]]' + path: $.first().first() +out: + return: SUCCEED + value: a +--- +test case: Query $.first().first().first() from [[["a", "b"]]] +in: + data: '[[["a", "b"]]]' + path: $.first().first().first() +out: + return: SUCCEED + value: a +--- +test case: Query $.books[*].price.min() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[*].price.min() +out: + return: SUCCEED + value: 8.95 +--- +test case: Query $..price.max() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..price.max() +out: + return: SUCCEED + value: 154.99 +--- +test case: Query $.books[?(@.category == "fiction")].price.avg() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == "fiction")].price.avg() +out: + return: SUCCEED + value: 14.99 +--- +test case: Query $.books[?(@.category == $.filters.xyz)].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.category == $.filters.xyz)].title +out: + return: SUCCEED +--- +test case: Query $.filters['no filters'] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.filters['no filters'] +out: + return: SUCCEED + value: no "filters" +--- +test case: Query $.services[?(@.active=="true")].servicegroup +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.services[?(@.active=="true")].servicegroup +out: + return: SUCCEED + value: '[1000,1001]' +--- +test case: Query $.services[?(@.active=="false")].servicegroup +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.services[?(@.active=="false")].servicegroup +out: + return: SUCCEED + value: '[1002]' +--- +test case: Query $.books[?(@.title =~ "[a-z")].title +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[?(@.title =~ "[a-z")].title +out: + return: FAIL +--- +test case: $..books[?(!@.isbn)] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..books[?(!@.isbn)] +out: + return: SUCCEED + value: | + [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95, + "id": 1 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99, + "id": 2 + } + ] +--- +test case: $..books[?(@.isbn)] +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $..books[?(@.isbn)] +out: + return: SUCCEED + value: | + [ + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99, + "id": 3 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99, + "id": 4 + } + ] +--- +test case: Query $.books[*].price.sum() +include: &include zbx_jsonpath_query.inc.yaml +in: + data: *include + path: $.books[*].price.sum() +out: + return: SUCCEED + value: 53.92 +--- +test case: Query $[?(@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] +in: + data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' + path: $[?(@.a)].id +out: + return: SUCCEED + values: '[1]' +--- +test case: Query $[?(!@.a)].id from [{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}] +in: + data: '[{"a":{"b":"c"}, "id":1}, {"x":{"y":"z"}, "id":2}]' + path: $[?(!@.a)].id +out: + return: SUCCEED + values: '[2]' +--- +test case: Query $[?(@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] +in: + data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' + path: $[?(@.a)].id +out: + return: SUCCEED + values: '[1]' +--- +test case: Query $[?(!@.a)].id from [{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}] +in: + data: '[{"a":["b","c"], "id":1}, {"x":["y","z"], "id":2}]' + path: $[?(!@.a)].id +out: + return: SUCCEED + values: '[2]' +... + diff --git a/tests/libs/zbxsysinfo/Makefile.am b/tests/libs/zbxsysinfo/Makefile.am index c1d29c7cd0e..09a946d78c3 100644 --- a/tests/libs/zbxsysinfo/Makefile.am +++ b/tests/libs/zbxsysinfo/Makefile.am @@ -58,6 +58,7 @@ parse_item_key_LDADD = \ $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ $(top_srcdir)/src/libs/zbxmedia/libzbxmedia.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ @@ -65,7 +66,6 @@ parse_item_key_LDADD = \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ - $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxhttp/libzbxhttp.a \ $(top_srcdir)/src/libs/zbxipcservice/libzbxipcservice.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ @@ -106,9 +106,9 @@ process_LDADD = \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ - $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxexec/libzbxexec.a \ $(top_srcdir)/src/libs/zbxmodules/libzbxmodules.a \ $(top_srcdir)/src/zabbix_agent/libzbxagent.a \ diff --git a/tests/libs/zbxsysinfo/common/Makefile.am b/tests/libs/zbxsysinfo/common/Makefile.am index 6315bf33f9e..2c0e9e7075c 100644 --- a/tests/libs/zbxsysinfo/common/Makefile.am +++ b/tests/libs/zbxsysinfo/common/Makefile.am @@ -26,6 +26,7 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ diff --git a/tests/libs/zbxsysinfo/linux/Makefile.am b/tests/libs/zbxsysinfo/linux/Makefile.am index 88f71778c4c..93ad477029f 100644 --- a/tests/libs/zbxsysinfo/linux/Makefile.am +++ b/tests/libs/zbxsysinfo/linux/Makefile.am @@ -34,6 +34,7 @@ COMMON_LIB_FILES = \ $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ diff --git a/tests/zabbix_server/preprocessor/Makefile.am b/tests/zabbix_server/preprocessor/Makefile.am index 032f2cbd215..b1fc815ae02 100644 --- a/tests/zabbix_server/preprocessor/Makefile.am +++ b/tests/zabbix_server/preprocessor/Makefile.am @@ -1,5 +1,5 @@ if SERVER -SERVER_tests = zbx_item_preproc +SERVER_tests = zbx_item_preproc if HAVE_LIBXML2 SERVER_tests += item_preproc_xpath @@ -53,4 +53,5 @@ item_preproc_xpath_LDADD += @SERVER_LIBS@ item_preproc_xpath_LDFLAGS = @SERVER_LDFLAGS@ item_preproc_xpath_CFLAGS = -I@top_srcdir@/tests @LIBXML2_CFLAGS@ + endif diff --git a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml index 8efec2282de..af39eec592b 100644 --- a/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml +++ b/tests/zabbix_server/preprocessor/zbx_item_preproc.yaml @@ -983,6 +983,87 @@ out: return: SUCCEED value: 2 --- +test case: jsonpath7 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b c":["one", "two", "three"]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b c'] +out: + return: SUCCEED + value: '["one", "two", "three"]' +--- +test case: jsonpath8 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b c":["one", "two \"2\"", 3]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b c'][1] +out: + return: SUCCEED + value: two "2" +--- +test case: jsonpath9 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b c":["one", "two \"2\"", 3]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b c'][2] +out: + return: SUCCEED + value: 3 +--- +test case: jsonpath10 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b":[1, 2, 3]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b'][3] +out: + return: FAIL +--- +test case: jsonpath11 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b":[1, 2, 3]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b'][ +out: + return: FAIL +--- +test case: jsonpath12 +in: + value: + value_type: ITEM_VALUE_TYPE_STR + time: 2017-10-29 03:15:00 +03:00 + data: |- + {"a":{"b":[1, 2, 3]}} + step: + type: ZBX_PREPROC_JSONPATH + params: $.a['b][3] +out: + return: FAIL +--- test case: validate_range(1, 5, 10) in: value: diff --git a/tests/zbxmockdata.c b/tests/zbxmockdata.c index a7f627905d1..cf369ec593e 100644 --- a/tests/zbxmockdata.c +++ b/tests/zbxmockdata.c @@ -162,6 +162,8 @@ static int zbx_yaml_include(yaml_document_t *dst_doc, yaml_node_pair_t *dst, con if (-1 != (index = zbx_yaml_add_node(dst_doc, &doc, src_root))) dst->value = index; } + else + printf("Cannot parse include file '%s'\n", filename); __real_fclose(fp); diff --git a/tests/zbxmockjson.c b/tests/zbxmockjson.c index fda1e0f0ccf..9f7233f9f69 100644 --- a/tests/zbxmockjson.c +++ b/tests/zbxmockjson.c @@ -24,6 +24,7 @@ #include "zbxmocktest.h" #include "zbxmockdata.h" #include "zbxmockassert.h" +#include "zbxmockjson.h" #define _FAIL(file, line, prefix, message, ...) \ \ @@ -112,7 +113,7 @@ static void json_flatten_object(struct zbx_json_parse *jp, const char *prefix, z { const char *pnext = NULL; char *path = NULL, key[MAX_STRING_LEN]; - size_t path_alloc = 0, path_offset = 0; + size_t path_alloc = 0, path_offset; while (NULL != (pnext = zbx_json_pair_next(jp, pnext, key, sizeof(key)))) { @@ -129,10 +130,9 @@ static void json_flatten_object(struct zbx_json_parse *jp, const char *prefix, z static void json_flatten_array(struct zbx_json_parse *jp, const char *parent, zbx_vector_ptr_pair_t *props) { - const char *pnext; - char *path, *value = NULL; - int index = 0; - + const char *pnext; + char *path, *value = NULL; + int index = 0; for (pnext = NULL; NULL != (pnext = zbx_json_next(jp, pnext));) { @@ -243,14 +243,14 @@ void __zbx_mock_assert_json_eq(const char *file, int line, const char *prefix_ms _FAIL(file, line, prefix_msg, "Did not expect key \"%s\"", pair_returned->first); } - for (i = 0; i < props_expected.values_num; i++) + for (i = 0; i < props_expected.values_num; i++) { zbx_free(props_expected.values[i].first); zbx_free(props_expected.values[i].second); } zbx_vector_ptr_pair_destroy(&props_expected); - for (i = 0; i < props_returned.values_num; i++) + for (i = 0; i < props_returned.values_num; i++) { zbx_free(props_returned.values[i].first); zbx_free(props_returned.values[i].second); diff --git a/tests/zbxmockjson.h b/tests/zbxmockjson.h index 9e4d4045fb8..ae3e248ac52 100644 --- a/tests/zbxmockjson.h +++ b/tests/zbxmockjson.h @@ -26,4 +26,4 @@ void __zbx_mock_assert_json_eq(const char *file, int line, const char *prefix_ms #define zbx_mock_assert_json_eq(prefix_msg, expected_value, returned_value) \ __zbx_mock_assert_json_eq(__FILE__, __LINE__, prefix_msg, expected_value, returned_value) -#endif /* BUILD_TESTS_ZBXMOCKDB_H_ */ +#endif |