diff --git a/include/maxscale/json_api.h b/include/maxscale/json_api.h index dfe5f1418..d3d15ad31 100644 --- a/include/maxscale/json_api.h +++ b/include/maxscale/json_api.h @@ -61,4 +61,13 @@ json_t* mxs_json_relationship(const char* host, const char* endpoint); */ void mxs_json_add_relation(json_t* rel, const char* id, const char* type); +/** + * @brief Return value at provided JSON Pointer + * + * @param json JSON object + * @param json_ptr JSON Pointer to object + * @return Pointed value or NULL if no value is found + */ +json_t* mxs_json_pointer(json_t* json, const char* json_ptr); + MXS_END_DECLS diff --git a/server/core/json_api.cc b/server/core/json_api.cc index 2c820bfc7..5e856af16 100644 --- a/server/core/json_api.cc +++ b/server/core/json_api.cc @@ -61,3 +61,73 @@ void mxs_json_add_relation(json_t* rel, const char* id, const char* type) json_object_set_new(obj, CN_TYPE, json_string(type)); json_array_append(data, obj); } + +static string grab_next_component(string* s) +{ + std::string& str = *s; + + while (str.length() > 0 && str[0] == '/') + { + str.erase(str.begin()); + } + + size_t pos = str.find("/"); + string rval; + + if (pos != string::npos) + { + rval = str.substr(0, pos); + str.erase(0, pos); + return rval; + } + else + { + rval = str; + str.erase(0); + } + + return rval; +} + +static bool is_integer(const string& str) +{ + char* end; + return strtol(str.c_str(), &end, 10) >= 0 && *end == '\0'; +} + +static json_t* mxs_json_pointer_internal(json_t* json, string str) +{ + json_t* rval = NULL; + string comp = grab_next_component(&str); + + if (comp.length() == 0) + { + return json; + } + + if (json_is_array(json) && is_integer(comp)) + { + size_t idx = strtol(comp.c_str(), NULL, 10); + + if (idx < json_array_size(json)) + { + rval = mxs_json_pointer_internal(json_array_get(json, idx), str); + } + } + else if (json_is_object(json)) + { + json_t* obj = json_object_get(json, comp.c_str()); + + if (obj) + { + rval = mxs_json_pointer_internal(obj, str); + } + } + + return rval; +} + +json_t* mxs_json_pointer(json_t* json, const char* json_ptr) +{ + return mxs_json_pointer_internal(json, json_ptr); +} diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 89ca4cc0a..33c47c1f6 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(testmaxscalepcre2 testmaxscalepcre2.cc) add_executable(testmodulecmd testmodulecmd.cc) add_executable(testconfig testconfig.cc) add_executable(trxboundaryparser_profile trxboundaryparser_profile.cc) +add_executable(testjson testjson.cc) target_link_libraries(test_atomic maxscale-common) target_link_libraries(test_adminusers maxscale-common) target_link_libraries(test_buffer maxscale-common) @@ -48,6 +49,7 @@ target_link_libraries(testmaxscalepcre2 maxscale-common) target_link_libraries(testmodulecmd maxscale-common) target_link_libraries(testconfig maxscale-common) target_link_libraries(trxboundaryparser_profile maxscale-common) +target_link_libraries(testjson maxscale-common) add_test(TestAtomic test_atomic) add_test(TestAdminUsers test_adminusers) add_test(TestBuffer test_buffer) @@ -79,6 +81,7 @@ add_test(TestTrxCompare_Select test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../.. add_test(TestTrxCompare_Set test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/set.test) add_test(TestTrxCompare_Update test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/update.test) add_test(TestTrxCompare_MaxScale test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/maxscale.test) +add_test(TestJson testjson) # This test requires external dependencies and thus cannot be run # as a part of the core test set diff --git a/server/core/test/testjson.cc b/server/core/test/testjson.cc new file mode 100644 index 000000000..48df4ba57 --- /dev/null +++ b/server/core/test/testjson.cc @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include + +#include + +#include +#include +#include + +using std::string; + +const char* test1_json = + "{" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/servers/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"server1\"," + " \"type\": \"servers\"," + " \"relationships\": {" + " \"services\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/services/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"RW-Split-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"SchemaRouter-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"RW-Split-Hint-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"Read-Connection-Router\"," + " \"type\": \"services\"" + " }" + " ]" + " }," + " \"monitors\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/monitors/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"MySQL-Monitor\"," + " \"type\": \"monitors\"" + " }" + " ]" + " }" + " }," + " \"attributes\": {" + " \"parameters\": {" + " \"address\": \"127.0.0.1\"," + " \"port\": 3000," + " \"protocol\": \"MySQLBackend\"" + " }," + " \"status\": \"Master, Running\"," + " \"version_string\": \"10.1.19-MariaDB-1~jessie\"," + " \"node_id\": 3000," + " \"master_id\": -1," + " \"replication_depth\": 0," + " \"slaves\": [" + " 3001," + " 3002," + " 3003" + " ]," + " \"statictics\": {" + " \"connections\": 0," + " \"total_connections\": 0," + " \"active_operations\": 0" + " }" + " }" + " }," + " {" + " \"id\": \"server2\"," + " \"type\": \"servers\"," + " \"relationships\": {" + " \"services\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/services/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"RW-Split-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"SchemaRouter-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"RW-Split-Hint-Router\"," + " \"type\": \"services\"" + " }" + " ]" + " }," + " \"monitors\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/monitors/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"MySQL-Monitor\"," + " \"type\": \"monitors\"" + " }" + " ]" + " }" + " }," + " \"attributes\": {" + " \"parameters\": {" + " \"address\": \"127.0.0.1\"," + " \"port\": 3001," + " \"protocol\": \"MySQLBackend\"" + " }," + " \"status\": \"Slave, Running\"," + " \"version_string\": \"10.1.19-MariaDB-1~jessie\"," + " \"node_id\": 3001," + " \"master_id\": 3000," + " \"replication_depth\": 1," + " \"slaves\": []," + " \"statictics\": {" + " \"connections\": 0," + " \"total_connections\": 0," + " \"active_operations\": 0" + " }" + " }" + " }," + " {" + " \"id\": \"server3\"," + " \"type\": \"servers\"," + " \"relationships\": {" + " \"services\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/services/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"RW-Split-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"SchemaRouter-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"RW-Split-Hint-Router\"," + " \"type\": \"services\"" + " }" + " ]" + " }," + " \"monitors\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/monitors/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"MySQL-Monitor\"," + " \"type\": \"monitors\"" + " }" + " ]" + " }" + " }," + " \"attributes\": {" + " \"parameters\": {" + " \"address\": \"127.0.0.1\"," + " \"port\": 3002," + " \"protocol\": \"MySQLBackend\"" + " }," + " \"status\": \"Slave, Running\"," + " \"version_string\": \"10.1.19-MariaDB-1~jessie\"," + " \"node_id\": 3002," + " \"master_id\": 3000," + " \"replication_depth\": 1," + " \"slaves\": []," + " \"statictics\": {" + " \"connections\": 0," + " \"total_connections\": 0," + " \"active_operations\": 0" + " }" + " }" + " }," + " {" + " \"id\": \"server4\"," + " \"type\": \"servers\"," + " \"relationships\": {" + " \"services\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/services/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"RW-Split-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"SchemaRouter-Router\"," + " \"type\": \"services\"" + " }," + " {" + " \"id\": \"RW-Split-Hint-Router\"," + " \"type\": \"services\"" + " }" + " ]" + " }," + " \"monitors\": {" + " \"links\": {" + " \"self\": \"http://localhost:8989/v1/monitors/\"" + " }," + " \"data\": [" + " {" + " \"id\": \"MySQL-Monitor\"," + " \"type\": \"monitors\"" + " }" + " ]" + " }" + " }," + " \"attributes\": {" + " \"parameters\": {" + " \"address\": \"127.0.0.1\"," + " \"port\": 3003," + " \"protocol\": \"MySQLBackend\"" + " }," + " \"status\": \"Slave, Running\"," + " \"version_string\": \"10.1.19-MariaDB-1~jessie\"," + " \"node_id\": 3003," + " \"master_id\": 3000," + " \"replication_depth\": 1," + " \"slaves\": []," + " \"statictics\": {" + " \"connections\": 0," + " \"total_connections\": 0," + " \"active_operations\": 0" + " }" + " }" + " }" + " ]" + "}"; + +int test1() +{ + json_error_t err = {}; + json_t* json = json_loads(test1_json, 0, &err); + + ss_dassert(json); + + ss_dassert(mxs_json_pointer(json, "") == json); + ss_dassert(mxs_json_pointer(json, "links") == json_object_get(json, "links")); + ss_dassert(json_is_string(mxs_json_pointer(json, "links/self"))); + + ss_dassert(mxs_json_pointer(json, "data") == json_object_get(json, "data")); + ss_dassert(json_is_array(mxs_json_pointer(json, "data"))); + + ss_dassert(json_is_object(mxs_json_pointer(json, "data/0"))); + ss_dassert(json_is_string(mxs_json_pointer(json, "data/0/id"))); + string s = json_string_value(mxs_json_pointer(json, "data/0/id")); + ss_dassert(s == "server1"); + + ss_dassert(json_is_object(mxs_json_pointer(json, "data/1"))); + ss_dassert(json_is_string(mxs_json_pointer(json, "data/1/id"))); + s = json_string_value(mxs_json_pointer(json, "data/1/id")); + ss_dassert(s == "server2"); + + ss_dassert(json_is_object(mxs_json_pointer(json, "data/0/attributes"))); + ss_dassert(json_is_object(mxs_json_pointer(json, "data/0/attributes/parameters"))); + ss_dassert(json_is_integer(mxs_json_pointer(json, "data/0/attributes/parameters/port"))); + int i = json_integer_value(mxs_json_pointer(json, "data/0/attributes/parameters/port")); + ss_dassert(i == 3000); + + ss_dassert(json_is_array(mxs_json_pointer(json, "data/0/attributes/slaves"))); + ss_dassert(json_array_size(mxs_json_pointer(json, "data/0/attributes/slaves")) == 3); + + return 0; +} + +int main(int argc, char** argv) +{ + test1(); + return 0; +}