From cf1f43dc9de5c66572d16cc89ca7b6e13511ad0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 3 Dec 2015 15:09:39 +0100 Subject: [PATCH] feat(influxdb): support for table queries, closes #3409, #3219 --- .../app/{panels/table => core}/table_model.ts | 23 +++------- public/app/panels/table/controller.ts | 20 ++++++++- .../app/panels/table/specs/renderer_specs.ts | 2 +- .../panels/table/specs/transformers_specs.ts | 13 +++--- public/app/panels/table/transformers.ts | 40 +++++++++++++++++- .../plugins/datasource/influxdb/datasource.js | 24 ++++++++--- .../datasource/influxdb/influx_query.ts | 2 + .../datasource/influxdb/influx_series.js | 42 ++++++++++++++++++- .../influxdb/partials/query.editor.html | 6 +++ .../plugins/datasource/influxdb/query_ctrl.js | 5 +++ .../influxdb/specs/influx_series_specs.ts | 23 ++++++++++ .../specs => test/core}/table_model_specs.ts | 2 +- 12 files changed, 166 insertions(+), 36 deletions(-) rename public/app/{panels/table => core}/table_model.ts (56%) rename public/{app/panels/table/specs => test/core}/table_model_specs.ts (95%) diff --git a/public/app/panels/table/table_model.ts b/public/app/core/table_model.ts similarity index 56% rename from public/app/panels/table/table_model.ts rename to public/app/core/table_model.ts index 1fa4007e6e3..7eb8d5ad92a 100644 --- a/public/app/panels/table/table_model.ts +++ b/public/app/core/table_model.ts @@ -1,12 +1,13 @@ -import {transformers} from './transformers'; -export class TableModel { +class TableModel { columns: any[]; rows: any[]; + type: string; constructor() { this.columns = []; this.rows = []; + this.type = 'table'; } sort(options) { @@ -33,20 +34,6 @@ export class TableModel { this.columns[options.col].desc = true; } } - - static transform(data, panel) { - var model = new TableModel(); - - if (!data || data.length === 0) { - return model; - } - - var transformer = transformers[panel.transform]; - if (!transformer) { - throw {message: 'Transformer ' + panel.transformer + ' not found'}; - } - - transformer.transform(data, panel, model); - return model; - } } + +export = TableModel; diff --git a/public/app/panels/table/controller.ts b/public/app/panels/table/controller.ts index 09e77108631..270e2f65a3d 100644 --- a/public/app/panels/table/controller.ts +++ b/public/app/panels/table/controller.ts @@ -5,7 +5,7 @@ import _ = require('lodash'); import moment = require('moment'); import PanelMeta = require('app/features/panel/panel_meta'); -import {TableModel} from './table_model'; +import {transformDataToTable} from './transformers'; export class TablePanelCtrl { @@ -104,7 +104,23 @@ export class TablePanelCtrl { }; $scope.render = function() { - $scope.table = TableModel.transform($scope.dataRaw, $scope.panel); + // automatically correct transform mode + // based on data + if ($scope.dataRaw && $scope.dataRaw.length) { + if ($scope.dataRaw[0].type === 'table') { + $scope.panel.transform = 'table'; + } else { + if ($scope.dataRaw[0].type === 'docs') { + $scope.panel.transform = 'json'; + } else { + if ($scope.panel.transform === 'table' || $scope.panel.transform === 'json') { + $scope.panel.transform = 'timeseries_to_rows'; + } + } + } + } + + $scope.table = transformDataToTable($scope.dataRaw, $scope.panel); $scope.table.sort($scope.panel.sort); panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw); }; diff --git a/public/app/panels/table/specs/renderer_specs.ts b/public/app/panels/table/specs/renderer_specs.ts index f8fdebb9ab0..f8af1baba17 100644 --- a/public/app/panels/table/specs/renderer_specs.ts +++ b/public/app/panels/table/specs/renderer_specs.ts @@ -1,6 +1,6 @@ import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; -import {TableModel} from '../table_model'; +import TableModel = require('app/core/table_model'); import {TableRenderer} from '../renderer'; describe('when rendering table', () => { diff --git a/public/app/panels/table/specs/transformers_specs.ts b/public/app/panels/table/specs/transformers_specs.ts index bb42b997d33..e3cdf44c8b2 100644 --- a/public/app/panels/table/specs/transformers_specs.ts +++ b/public/app/panels/table/specs/transformers_specs.ts @@ -1,7 +1,6 @@ import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; -import {TableModel} from '../table_model'; -import {transformers} from '../transformers'; +import {transformers, transformDataToTable} from '../transformers'; describe('when transforming time series table', () => { var table; @@ -26,7 +25,7 @@ describe('when transforming time series table', () => { }; beforeEach(() => { - table = TableModel.transform(timeSeries, panel); + table = transformDataToTable(timeSeries, panel); }); it('should return 3 rows', () => { @@ -51,7 +50,7 @@ describe('when transforming time series table', () => { }; beforeEach(() => { - table = TableModel.transform(timeSeries, panel); + table = transformDataToTable(timeSeries, panel); }); it ('should return 3 columns', () => { @@ -80,7 +79,7 @@ describe('when transforming time series table', () => { }; beforeEach(() => { - table = TableModel.transform(timeSeries, panel); + table = transformDataToTable(timeSeries, panel); }); it('should return 2 rows', () => { @@ -133,7 +132,7 @@ describe('when transforming time series table', () => { describe('transform', function() { beforeEach(() => { - table = TableModel.transform(rawData, panel); + table = transformDataToTable(rawData, panel); }); it ('should return 2 columns', () => { @@ -164,7 +163,7 @@ describe('when transforming time series table', () => { ]; beforeEach(() => { - table = TableModel.transform(rawData, panel); + table = transformDataToTable(rawData, panel); }); it ('should return 4 columns', () => { diff --git a/public/app/panels/table/transformers.ts b/public/app/panels/table/transformers.ts index a4d0d4395c5..843eb83c034 100644 --- a/public/app/panels/table/transformers.ts +++ b/public/app/panels/table/transformers.ts @@ -4,6 +4,7 @@ import moment = require('moment'); import _ = require('lodash'); import flatten = require('app/core/utils/flatten'); import TimeSeries = require('app/core/time_series'); +import TableModel = require('app/core/table_model'); var transformers = {}; @@ -136,6 +137,27 @@ transformers['annotations'] = { } }; +transformers['table'] = { + description: 'Table', + getColumns: function(data) { + if (!data || data.length === 0) { + return []; + } + }, + transform: function(data, panel, model) { + if (!data || data.length === 0) { + return; + } + + if (data[0].type !== 'table') { + throw {message: 'Query result is not in table format, try using another transform.'}; + } + + model.columns = data[0].columns; + model.rows = data[0].rows; + } +}; + transformers['json'] = { description: 'JSON Data', getColumns: function(data) { @@ -197,4 +219,20 @@ transformers['json'] = { } }; -export {transformers} +function transformDataToTable(data, panel) { + var model = new TableModel(); + + if (!data || data.length === 0) { + return model; + } + + var transformer = transformers[panel.transform]; + if (!transformer) { + throw {message: 'Transformer ' + panel.transformer + ' not found'}; + } + + transformer.transform(data, panel, model); + return model; +} + +export {transformers, transformDataToTable} diff --git a/public/app/plugins/datasource/influxdb/datasource.js b/public/app/plugins/datasource/influxdb/datasource.js index 9adabe9a83f..a23937cd74c 100644 --- a/public/app/plugins/datasource/influxdb/datasource.js +++ b/public/app/plugins/datasource/influxdb/datasource.js @@ -53,6 +53,7 @@ function (angular, _, dateMath, InfluxSeries, InfluxQuery) { // replace templated variables allQueries = templateSrv.replace(allQueries, options.scopedVars); + return this._seriesQuery(allQueries).then(function(data) { if (!data || !data.results) { return []; @@ -63,13 +64,26 @@ function (angular, _, dateMath, InfluxSeries, InfluxQuery) { var result = data.results[i]; if (!result || !result.series) { continue; } - var alias = (queryTargets[i] || {}).alias; + var target = queryTargets[i]; + var alias = target.alias; if (alias) { - alias = templateSrv.replace(alias, options.scopedVars); + alias = templateSrv.replace(target.alias, options.scopedVars); } - var targetSeries = new InfluxSeries({ series: data.results[i].series, alias: alias }).getTimeSeries(); - for (y = 0; y < targetSeries.length; y++) { - seriesList.push(targetSeries[y]); + + var influxSeries = new InfluxSeries({ series: data.results[i].series, alias: alias }); + + switch(target.resultFormat) { + case 'table': { + seriesList.push(influxSeries.getTable()); + break; + } + default: { + var timeSeries = influxSeries.getTimeSeries(); + for (y = 0; y < timeSeries.length; y++) { + seriesList.push(timeSeries[y]); + } + break; + } } } diff --git a/public/app/plugins/datasource/influxdb/influx_query.ts b/public/app/plugins/datasource/influxdb/influx_query.ts index 34b86e16930..f50627c7f89 100644 --- a/public/app/plugins/datasource/influxdb/influx_query.ts +++ b/public/app/plugins/datasource/influxdb/influx_query.ts @@ -12,6 +12,8 @@ class InfluxQuery { constructor(target) { this.target = target; + target.dsType = 'influxdb'; + target.resultFormat = target.resultFormat || 'time_series'; target.tags = target.tags || []; target.groupBy = target.groupBy || [ {type: 'time', params: ['$interval']}, diff --git a/public/app/plugins/datasource/influxdb/influx_series.js b/public/app/plugins/datasource/influxdb/influx_series.js index fff3536b5e8..63495ffb9b0 100644 --- a/public/app/plugins/datasource/influxdb/influx_series.js +++ b/public/app/plugins/datasource/influxdb/influx_series.js @@ -1,7 +1,8 @@ define([ 'lodash', + 'app/core/table_model', ], -function (_) { +function (_, TableModel) { 'use strict'; function InfluxSeries(options) { @@ -108,5 +109,44 @@ function (_) { return list; }; + p.getTable = function() { + var table = new TableModel(); + var self = this; + var i, j; + + if (self.series.length === 0) { + return table; + } + + _.each(self.series, function(series, seriesIndex) { + + if (seriesIndex === 0) { + table.columns.push({text: 'Time', type: 'time'}); + _.each(_.keys(series.tags), function(key) { + table.columns.push({text: key}); + }); + for (j = 1; j < series.columns.length; j++) { + table.columns.push({text: series.columns[j]}); + } + } + + if (series.values) { + for (i = 0; i < series.values.length; i++) { + var values = series.values[i]; + if (series.tags) { + for (var key in series.tags) { + if (series.tags.hasOwnProperty(key)) { + values.splice(1, 0, series.tags[key]); + } + } + } + table.rows.push(values); + } + } + }); + + return table; + }; + return InfluxSeries; }); diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index 0aeb8a44224..2b9f7e1a620 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -103,6 +103,12 @@
  • +
  • + Format as +
  • +
  • + +
  • diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.js b/public/app/plugins/datasource/influxdb/query_ctrl.js index 38f87ecd84e..52b7e7f1b7a 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.js +++ b/public/app/plugins/datasource/influxdb/query_ctrl.js @@ -20,6 +20,11 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) { $scope.queryModel = new InfluxQuery($scope.target); $scope.queryBuilder = new InfluxQueryBuilder($scope.target); $scope.groupBySegment = uiSegmentSrv.newPlusButton(); + $scope.resultFormats = [ + {text: 'Time series', value: 'time_series'}, + {text: 'Table', value: 'table'}, + {text: 'JSON field', value: 'json_field'}, + ]; if (!$scope.target.measurement) { $scope.measurementSegment = uiSegmentSrv.newSelectMeasurement(); diff --git a/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts index c8c127ed759..8352e41d99a 100644 --- a/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts +++ b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts @@ -186,5 +186,28 @@ describe('when generating timeseries from influxdb response', function() { }); }); + describe('given table response', function() { + var options = { + alias: '', + series: [ + { + name: 'app.prod.server1.count', + tags: {}, + columns: ['time', 'datacenter', 'value'], + values: [[1431946625000, 'America', 10], [1431946626000, 'EU', 12]] + } + ] + }; + + it('should return table', function() { + var series = new InfluxSeries(options); + var table = series.getTable(); + + expect(table.type).to.be('table'); + expect(table.columns.length).to.be(3); + expect(table.rows[0]).to.eql([1431946625000, 'America', 10]);; + }); + }); + }); diff --git a/public/app/panels/table/specs/table_model_specs.ts b/public/test/core/table_model_specs.ts similarity index 95% rename from public/app/panels/table/specs/table_model_specs.ts rename to public/test/core/table_model_specs.ts index ad515835730..8cdeb04f8d0 100644 --- a/public/app/panels/table/specs/table_model_specs.ts +++ b/public/test/core/table_model_specs.ts @@ -1,6 +1,6 @@ import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; -import {TableModel} from '../table_model'; +import TableModel = require('app/core/table_model'); describe('when sorting table desc', () => { var table;