From 914549be3bfffdd3ad4b2599ed1e771d857512da Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sun, 2 Nov 2014 19:04:42 +0100 Subject: [PATCH] store: Let JSON formatter know about the to-be-formatted object type. When formatting services or metrics, expect those objects to be emitted on the highest level and then create the host object for them first (once for a series of objects belonging to the same host). --- src/core/store_json.c | 76 +++++++++++++---- src/frontend/query.c | 2 +- src/include/core/store.h | 5 +- t/unit/core/store_json_test.c | 148 +++++++++++++++++++++++++++++++--- 4 files changed, 202 insertions(+), 29 deletions(-) diff --git a/src/core/store_json.c b/src/core/store_json.c index b4b7b6a..493ac9f 100644 --- a/src/core/store_json.c +++ b/src/core/store_json.c @@ -53,6 +53,10 @@ struct sdb_store_json_formatter { int context[8]; size_t current; + /* The current host context when processing non-host objects */ + sdb_store_obj_t *current_host; + + int type; int flags; }; @@ -69,6 +73,17 @@ json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj) assert(f && obj); + if ((f->type != SDB_HOST) && (f->type == obj->type)) { + /* create the host for the current entry first */ + assert(obj->parent && (obj->parent->type == SDB_HOST)); + if (f->current_host != obj->parent) { + json_emit(f, obj->parent); + sdb_strbuf_append(f->buf, ", \"%ss\": [", + SDB_STORE_TYPE_TO_NAME(obj->type)); + f->current_host = obj->parent; + } + } + sdb_strbuf_append(f->buf, "{\"name\": \"%s\", ", SDB_OBJ(obj)->name); if (obj->type == SDB_ATTRIBUTE) { char tmp[sdb_data_strlen(&ATTR(obj)->value) + 1]; @@ -107,13 +122,16 @@ json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj) */ sdb_store_json_formatter_t * -sdb_store_json_formatter(sdb_strbuf_t *buf, int flags) +sdb_store_json_formatter(sdb_strbuf_t *buf, int type, int flags) { sdb_store_json_formatter_t *f; if (! buf) return NULL; + if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) + return NULL; + f = calloc(1, sizeof(*f)); if (! f) return NULL; @@ -121,6 +139,10 @@ sdb_store_json_formatter(sdb_strbuf_t *buf, int flags) f->buf = buf; f->context[0] = 0; f->current = 0; + + f->current_host = NULL; + + f->type = type; f->flags = flags; return f; } /* sdb_store_json_formatter */ @@ -131,15 +153,43 @@ sdb_store_json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj) if ((! f) || (! obj)) return -1; - /* first host */ + if ((f->type != SDB_HOST) && (obj->type == SDB_HOST)) { + sdb_log(SDB_LOG_ERR, "store: Unexpected object of type host " + "during %s JSON serialization", + SDB_STORE_TYPE_TO_NAME(f->type)); + return -1; + } + + /* first top-level object */ if (! f->context[0]) { + if (obj->type != f->type) { + sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s " + "as the first element during %s JSON serialization", + SDB_STORE_TYPE_TO_NAME(obj->type), + SDB_STORE_TYPE_TO_NAME(f->type)); + return -1; + } if (f->flags & SDB_WANT_ARRAY) sdb_strbuf_append(f->buf, "["); assert(f->current == 0); - f->context[0] = SDB_HOST; + f->context[0] = obj->type; return json_emit(f, obj); } + if ((f->current >= 1) && (obj->type != SDB_ATTRIBUTE)) { + /* new entry of a previous type or a new type on the same level; + * rewind to the right state */ + while (f->current > 0) { + if (f->context[f->current] == obj->type) + break; + sdb_strbuf_append(f->buf, "}]"); + --f->current; + } + } + if ((obj->type == f->type) && (f->type != SDB_HOST)) + if (obj->parent != f->current_host) + sdb_strbuf_append(f->buf, "}]"); + if (obj->type == f->context[f->current]) { /* new entry of the same type */ sdb_strbuf_append(f->buf, "},"); @@ -153,19 +203,6 @@ sdb_store_json_emit(sdb_store_json_formatter_t *f, sdb_store_obj_t *obj) SDB_STORE_TYPE_TO_NAME(obj->type)); ++f->current; } - else if (f->current >= 1) { - /* new entry of a previous type or a new type on the same level - * -> rewind to the right state and then handle the new object */ - assert(obj->type != SDB_ATTRIBUTE); - while (f->current > 0) { - if (f->context[f->current] == obj->type) - break; - assert(f->context[f->current] != SDB_HOST); - sdb_strbuf_append(f->buf, "}]"); - --f->current; - } - return sdb_store_json_emit(f, obj); - } else { sdb_log(SDB_LOG_ERR, "store: Unexpected object of type %s " "on level %zu during JSON serialization", @@ -244,8 +281,13 @@ sdb_store_json_finish(sdb_store_json_formatter_t *f) sdb_strbuf_append(f->buf, "}]"); --f->current; } + if (f->context[0] != SDB_HOST) { + assert(f->type != SDB_HOST); + sdb_strbuf_append(f->buf, "}]}"); + } + else + sdb_strbuf_append(f->buf, "}"); - sdb_strbuf_append(f->buf, "}"); if (f->flags & SDB_WANT_ARRAY) sdb_strbuf_append(f->buf, "]"); return 0; diff --git a/src/frontend/query.c b/src/frontend/query.c index 1ef36b3..1cb32dd 100644 --- a/src/frontend/query.c +++ b/src/frontend/query.c @@ -390,7 +390,7 @@ sdb_fe_exec_lookup(sdb_conn_t *conn, int type, sdb_strbuf_sprintf(conn->errbuf, "Out of memory"); return -1; } - f = sdb_store_json_formatter(buf, SDB_WANT_ARRAY); + f = sdb_store_json_formatter(buf, type, SDB_WANT_ARRAY); if (! f) { char errbuf[1024]; sdb_log(SDB_LOG_ERR, "frontend: Failed to create " diff --git a/src/include/core/store.h b/src/include/core/store.h index df959ea..c25c278 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -623,10 +623,11 @@ sdb_store_host_tojson(sdb_store_obj_t *host, sdb_strbuf_t *buf, /* * sdb_store_json_formatter: - * Create a JSON formatter writing to the specified buffer. + * Create a JSON formatter for the specified object types writing to the + * specified buffer. */ sdb_store_json_formatter_t * -sdb_store_json_formatter(sdb_strbuf_t *buf, int flags); +sdb_store_json_formatter(sdb_strbuf_t *buf, int type, int flags); /* * sdb_store_json_emit: diff --git a/t/unit/core/store_json_test.c b/t/unit/core/store_json_test.c index c544633..d7fad58 100644 --- a/t/unit/core/store_json_test.c +++ b/t/unit/core/store_json_test.c @@ -130,10 +130,12 @@ START_TEST(test_store_tojson) int field; sdb_data_t value; } filter; + int type; int (*f)(sdb_store_obj_t *, sdb_store_matcher_t *, void *); const char *expected; } golden_data[] = { - { { NULL, 0, SDB_DATA_INIT }, scan_tojson_full, + { { NULL, 0, SDB_DATA_INIT }, + SDB_HOST, scan_tojson_full, "[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " @@ -185,7 +187,8 @@ START_TEST(test_store_tojson) "]}" "]}" "]" }, - { { NULL, 0, SDB_DATA_INIT }, scan_tojson, + { { NULL, 0, SDB_DATA_INIT }, + SDB_HOST, scan_tojson, "[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}," @@ -193,13 +196,15 @@ START_TEST(test_store_tojson) "\"update_interval\": \"0s\", \"backends\": []}" "]" }, { { sdb_store_eq_matcher, SDB_FIELD_NAME, - { SDB_TYPE_STRING, { .string = "h1" } } }, scan_tojson_full, + { SDB_TYPE_STRING, { .string = "h1" } } }, + SDB_HOST, scan_tojson_full, "[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}" "]" }, { { sdb_store_gt_matcher, SDB_FIELD_LAST_UPDATE, - { SDB_TYPE_DATETIME, { .datetime = 1 } } }, scan_tojson_full, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, + SDB_HOST, scan_tojson_full, "[" "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " @@ -215,7 +220,8 @@ START_TEST(test_store_tojson) "]}" "]" }, { { sdb_store_le_matcher, SDB_FIELD_LAST_UPDATE, - { SDB_TYPE_DATETIME, { .datetime = 1 } } }, scan_tojson_full, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, + SDB_HOST, scan_tojson_full, "[" "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": [], " @@ -231,13 +237,136 @@ START_TEST(test_store_tojson) "]}" "]" }, { { sdb_store_ge_matcher, SDB_FIELD_LAST_UPDATE, - { SDB_TYPE_DATETIME, { .datetime = 3 } } }, scan_tojson_full, + { SDB_TYPE_DATETIME, { .datetime = 3 } } }, + SDB_HOST, scan_tojson_full, "[" "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " "\"update_interval\": \"0s\", \"backends\": []}" "]" }, { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE, - { SDB_TYPE_DATETIME, { .datetime = 0 } } }, scan_tojson_full, + { SDB_TYPE_DATETIME, { .datetime = 0 } } }, + SDB_HOST, scan_tojson_full, + "[]" }, + + { { NULL, 0, SDB_DATA_INIT }, + SDB_SERVICE, scan_tojson_full, + "[" + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"services\": [" + "{\"name\": \"s1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "{\"name\": \"s2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [" + "{\"name\": \"k1\", \"value\": 123, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "{\"name\": \"k2\", \"value\": 4711, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]}" + "]" }, + { { NULL, 0, SDB_DATA_INIT }, + SDB_SERVICE, scan_tojson, + "[" + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"services\": [" + "{\"name\": \"s1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "{\"name\": \"s2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]" }, + { { sdb_store_gt_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, + SDB_SERVICE, scan_tojson_full, + "[" + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"services\": [" + "{\"name\": \"s2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [" + "{\"name\": \"k1\", \"value\": 123, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]}" + "]" }, + { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 0 } } }, + SDB_SERVICE, scan_tojson_full, + "[]" }, + { { NULL, 0, SDB_DATA_INIT }, + SDB_METRIC, scan_tojson_full, + "[" + "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"metrics\": [" + "{\"name\": \"m1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"attributes\": [" + "{\"name\": \"k3\", \"value\": 42, " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}," + "{\"name\": \"m2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}," + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"metrics\": [" + "{\"name\": \"m1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]" }, + { { NULL, 0, SDB_DATA_INIT }, + SDB_METRIC, scan_tojson, + "[" + "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"metrics\": [" + "{\"name\": \"m1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}," + "{\"name\": \"m2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}," + "{\"name\": \"h2\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"metrics\": [" + "{\"name\": \"m1\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]" }, + { { sdb_store_le_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 1 } } }, + SDB_METRIC, scan_tojson_full, + "[" + "{\"name\": \"h1\", \"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": [], " + "\"metrics\": [" + "{\"name\": \"m2\", " + "\"last_update\": \"1970-01-01 00:00:00 +0000\", " + "\"update_interval\": \"0s\", \"backends\": []}" + "]}" + "]" }, + { { sdb_store_lt_matcher, SDB_FIELD_LAST_UPDATE, + { SDB_TYPE_DATETIME, { .datetime = 0 } } }, + SDB_METRIC, scan_tojson_full, "[]" }, }; @@ -272,10 +401,11 @@ START_TEST(test_store_tojson) } sdb_strbuf_clear(buf); - f = sdb_store_json_formatter(buf, SDB_WANT_ARRAY); + f = sdb_store_json_formatter(buf, + golden_data[i].type, SDB_WANT_ARRAY); assert(f); - status = sdb_store_scan(SDB_HOST, /* m = */ NULL, filter, + status = sdb_store_scan(golden_data[i].type, /* m = */ NULL, filter, golden_data[i].f, f); fail_unless(status == 0, "sdb_store_scan(HOST, ..., tojson) = %d; expected: 0", -- 2.30.2