// SPDX-License-Identifier: GPL-3.0-or-later #include "web/api/web_api_v1.h" #include "database/storage_engine.h" void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) { rrdset2json(st, wb, NULL, NULL, 0); } const char *rrdr_format_to_string(DATASOURCE_FORMAT format) { switch(format) { case DATASOURCE_JSON: return DATASOURCE_FORMAT_JSON; case DATASOURCE_JSON2: return DATASOURCE_FORMAT_JSON2; case DATASOURCE_DATATABLE_JSON: return DATASOURCE_FORMAT_DATATABLE_JSON; case DATASOURCE_DATATABLE_JSONP: return DATASOURCE_FORMAT_DATATABLE_JSONP; case DATASOURCE_JSONP: return DATASOURCE_FORMAT_JSONP; case DATASOURCE_SSV: return DATASOURCE_FORMAT_SSV; case DATASOURCE_CSV: return DATASOURCE_FORMAT_CSV; case DATASOURCE_TSV: return DATASOURCE_FORMAT_TSV; case DATASOURCE_HTML: return DATASOURCE_FORMAT_HTML; case DATASOURCE_JS_ARRAY: return DATASOURCE_FORMAT_JS_ARRAY; case DATASOURCE_SSV_COMMA: return DATASOURCE_FORMAT_SSV_COMMA; default: return "unknown"; } } int rrdset2value_api_v1( RRDSET *st , BUFFER *wb , NETDATA_DOUBLE *n , const char *dimensions , size_t points , time_t after , time_t before , RRDR_TIME_GROUPING group_method , const char *group_options , time_t resampling_time , uint32_t options , time_t *db_after , time_t *db_before , size_t *db_points_read , size_t *db_points_per_tier , size_t *result_points_generated , int *value_is_null , NETDATA_DOUBLE *anomaly_rate , time_t timeout , size_t tier , QUERY_SOURCE query_source , STORAGE_PRIORITY priority ) { int ret = HTTP_RESP_INTERNAL_SERVER_ERROR; ONEWAYALLOC *owa = onewayalloc_create(0); RRDR *r = rrd2rrdr_legacy( owa, st, points, after, before, group_method, resampling_time, options, dimensions, group_options, timeout, tier, query_source, priority); if(!r) { if(value_is_null) *value_is_null = 1; ret = HTTP_RESP_INTERNAL_SERVER_ERROR; goto cleanup; } if(db_points_read) *db_points_read += r->stats.db_points_read; if(db_points_per_tier) { for(size_t t = 0; t < storage_tiers ;t++) db_points_per_tier[t] += r->internal.qt->db.tiers[t].points; } if(result_points_generated) *result_points_generated += r->stats.result_points_generated; if(rrdr_rows(r) == 0) { if(db_after) *db_after = 0; if(db_before) *db_before = 0; if(value_is_null) *value_is_null = 1; ret = HTTP_RESP_BAD_REQUEST; goto cleanup; } if(wb) { if (r->view.flags & RRDR_RESULT_FLAG_RELATIVE) buffer_no_cacheable(wb); else if (r->view.flags & RRDR_RESULT_FLAG_ABSOLUTE) buffer_cacheable(wb); } if(db_after) *db_after = r->view.after; if(db_before) *db_before = r->view.before; long i = (!(options & RRDR_OPTION_REVERSED))?(long)rrdr_rows(r) - 1:0; *n = rrdr2value(r, i, options, value_is_null, anomaly_rate); ret = HTTP_RESP_OK; cleanup: rrdr_free(owa, r); onewayalloc_destroy(owa); return ret; } static inline void buffer_json_member_add_key_only(BUFFER *wb, const char *key) { buffer_print_json_comma_newline_spacing(wb); buffer_print_json_key(wb, key); buffer_fast_strcat(wb, ":", 1); wb->json.stack[wb->json.depth].count++; } static inline void buffer_json_member_add_string_open(BUFFER *wb, const char *key) { buffer_json_member_add_key_only(wb, key); buffer_strcat(wb, wb->json.value_quote); } static inline void buffer_json_member_add_string_close(BUFFER *wb) { buffer_strcat(wb, wb->json.value_quote); } int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *latest_timestamp) { wrapper_begin_t wrapper_begin = rrdr_json_wrapper_begin; wrapper_end_t wrapper_end = rrdr_json_wrapper_end; if(qt->request.version == 2) { wrapper_begin = rrdr_json_wrapper_begin2; wrapper_end = rrdr_json_wrapper_end2; } RRDR *r = rrd2rrdr(owa, qt); if(!r) { buffer_strcat(wb, "Cannot generate output with these parameters on this chart."); return HTTP_RESP_INTERNAL_SERVER_ERROR; } if (r->view.flags & RRDR_RESULT_FLAG_CANCEL) { rrdr_free(owa, r); return HTTP_RESP_BACKEND_FETCH_FAILED; } if(r->view.flags & RRDR_RESULT_FLAG_RELATIVE) buffer_no_cacheable(wb); else if(r->view.flags & RRDR_RESULT_FLAG_ABSOLUTE) buffer_cacheable(wb); if(latest_timestamp && rrdr_rows(r) > 0) *latest_timestamp = r->view.before; DATASOURCE_FORMAT format = qt->request.format; RRDR_OPTIONS options = qt->window.options; switch(format) { case DATASOURCE_SSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); rrdr2ssv(r, wb, options, "", " ", ""); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_PLAIN; rrdr2ssv(r, wb, options, "", " ", ""); } break; case DATASOURCE_SSV_COMMA: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); rrdr2ssv(r, wb, options, "", ",", ""); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_PLAIN; rrdr2ssv(r, wb, options, "", ",", ""); } break; case DATASOURCE_JS_ARRAY: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_array(wb, "result"); rrdr2ssv(r, wb, options, "", ",", ""); buffer_json_array_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_APPLICATION_JSON; rrdr2ssv(r, wb, options, "[", ",", "]"); } break; case DATASOURCE_CSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); rrdr2csv(r, wb, format, options, "", ",", "\\n", ""); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_PLAIN; rrdr2csv(r, wb, format, options, "", ",", "\r\n", ""); } break; case DATASOURCE_CSV_MARKDOWN: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); rrdr2csv(r, wb, format, options, "", "|", "\\n", ""); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_PLAIN; rrdr2csv(r, wb, format, options, "", "|", "\r\n", ""); } break; case DATASOURCE_CSV_JSON_ARRAY: wb->content_type = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) { wrapper_begin(r, wb); buffer_json_member_add_array(wb, "result"); rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n"); buffer_json_array_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_APPLICATION_JSON; buffer_strcat(wb, "[\n"); rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n"); buffer_strcat(wb, "\n]"); } break; case DATASOURCE_TSV: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); rrdr2csv(r, wb, format, options, "", "\t", "\\n", ""); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_PLAIN; rrdr2csv(r, wb, format, options, "", "\t", "\r\n", ""); } break; case DATASOURCE_HTML: if(options & RRDR_OPTION_JSON_WRAP) { wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); buffer_json_member_add_string_open(wb, "result"); buffer_strcat(wb, "\\n
\\n\\n"); rrdr2csv(r, wb, format, options, "\\n", ""); buffer_strcat(wb, "
", "", "
\\n
\\n\\n"); buffer_json_member_add_string_close(wb); wrapper_end(r, wb); } else { wb->content_type = CT_TEXT_HTML; buffer_strcat(wb, "\n
\n\n"); rrdr2csv(r, wb, format, options, "\n", ""); buffer_strcat(wb, "
", "", "
\n
\n\n"); } break; case DATASOURCE_DATATABLE_JSONP: wb->content_type = CT_APPLICATION_X_JAVASCRIPT; if(options & RRDR_OPTION_JSON_WRAP) { wrapper_begin(r, wb); buffer_json_member_add_key_only(wb, "result"); } rrdr2json(r, wb, options, 1); if(options & RRDR_OPTION_JSON_WRAP) wrapper_end(r, wb); break; case DATASOURCE_DATATABLE_JSON: wb->content_type = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) { wrapper_begin(r, wb); buffer_json_member_add_key_only(wb, "result"); } rrdr2json(r, wb, options, 1); if(options & RRDR_OPTION_JSON_WRAP) wrapper_end(r, wb); break; case DATASOURCE_JSONP: wb->content_type = CT_APPLICATION_X_JAVASCRIPT; if(options & RRDR_OPTION_JSON_WRAP) { wrapper_begin(r, wb); buffer_json_member_add_key_only(wb, "result"); } rrdr2json(r, wb, options, 0); if(options & RRDR_OPTION_JSON_WRAP) wrapper_end(r, wb); break; case DATASOURCE_JSON: default: wb->content_type = CT_APPLICATION_JSON; if(options & RRDR_OPTION_JSON_WRAP) { wrapper_begin(r, wb); buffer_json_member_add_key_only(wb, "result"); } rrdr2json(r, wb, options, 0); if(options & RRDR_OPTION_JSON_WRAP) { if (options & RRDR_OPTION_RETURN_JWAR) { buffer_json_member_add_key_only(wb, "anomaly_rates"); rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_AR, false); } wrapper_end(r, wb); } break; case DATASOURCE_JSON2: wb->content_type = CT_APPLICATION_JSON; wrapper_begin(r, wb); rrdr2json_v2(r, wb); wrapper_end(r, wb); break; } rrdr_free(owa, r); return HTTP_RESP_OK; }