mirror of https://github.com/grafana/grafana
parent
05dfccbb74
commit
eecf844ca2
@ -1,220 +0,0 @@ |
|||||||
define([ |
|
||||||
'angular', |
|
||||||
'lodash', |
|
||||||
'app/core/utils/datemath', |
|
||||||
'./influx_series', |
|
||||||
'./influx_query', |
|
||||||
], |
|
||||||
function (angular, _, dateMath, InfluxSeries, InfluxQuery) { |
|
||||||
'use strict'; |
|
||||||
|
|
||||||
InfluxQuery = InfluxQuery.default; |
|
||||||
|
|
||||||
/** @ngInject */ |
|
||||||
function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) { |
|
||||||
this.type = 'influxdb'; |
|
||||||
this.urls = _.map(instanceSettings.url.split(','), function(url) { |
|
||||||
return url.trim(); |
|
||||||
}); |
|
||||||
|
|
||||||
this.username = instanceSettings.username; |
|
||||||
this.password = instanceSettings.password; |
|
||||||
this.name = instanceSettings.name; |
|
||||||
this.database = instanceSettings.database; |
|
||||||
this.basicAuth = instanceSettings.basicAuth; |
|
||||||
|
|
||||||
this.supportAnnotations = true; |
|
||||||
this.supportMetrics = true; |
|
||||||
|
|
||||||
this.query = function(options) { |
|
||||||
var timeFilter = getTimeFilter(options); |
|
||||||
var queryTargets = []; |
|
||||||
var i, y; |
|
||||||
|
|
||||||
var allQueries = _.map(options.targets, function(target) { |
|
||||||
if (target.hide) { return []; } |
|
||||||
|
|
||||||
queryTargets.push(target); |
|
||||||
|
|
||||||
// build query
|
|
||||||
var queryModel = new InfluxQuery(target); |
|
||||||
var query = queryModel.render(); |
|
||||||
query = query.replace(/\$interval/g, (target.interval || options.interval)); |
|
||||||
return query; |
|
||||||
|
|
||||||
}).join("\n"); |
|
||||||
|
|
||||||
// replace grafana variables
|
|
||||||
allQueries = allQueries.replace(/\$timeFilter/g, timeFilter); |
|
||||||
|
|
||||||
// replace templated variables
|
|
||||||
allQueries = templateSrv.replace(allQueries, options.scopedVars); |
|
||||||
|
|
||||||
return this._seriesQuery(allQueries).then(function(data) { |
|
||||||
if (!data || !data.results) { |
|
||||||
return []; |
|
||||||
} |
|
||||||
|
|
||||||
var seriesList = []; |
|
||||||
for (i = 0; i < data.results.length; i++) { |
|
||||||
var result = data.results[i]; |
|
||||||
if (!result || !result.series) { continue; } |
|
||||||
|
|
||||||
var target = queryTargets[i]; |
|
||||||
var alias = target.alias; |
|
||||||
if (alias) { |
|
||||||
alias = templateSrv.replace(target.alias, options.scopedVars); |
|
||||||
} |
|
||||||
|
|
||||||
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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return { data: seriesList }; |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.annotationQuery = function(options) { |
|
||||||
var timeFilter = getTimeFilter({rangeRaw: options.rangeRaw}); |
|
||||||
var query = options.annotation.query.replace('$timeFilter', timeFilter); |
|
||||||
query = templateSrv.replace(query); |
|
||||||
|
|
||||||
return this._seriesQuery(query).then(function(data) { |
|
||||||
if (!data || !data.results || !data.results[0]) { |
|
||||||
throw { message: 'No results in response from InfluxDB' }; |
|
||||||
} |
|
||||||
return new InfluxSeries({series: data.results[0].series, annotation: options.annotation}).getAnnotations(); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.metricFindQuery = function (query) { |
|
||||||
var interpolated; |
|
||||||
try { |
|
||||||
interpolated = templateSrv.replace(query); |
|
||||||
} |
|
||||||
catch (err) { |
|
||||||
return $q.reject(err); |
|
||||||
} |
|
||||||
|
|
||||||
return this._seriesQuery(interpolated).then(function (results) { |
|
||||||
if (!results || results.results.length === 0) { return []; } |
|
||||||
|
|
||||||
var influxResults = results.results[0]; |
|
||||||
if (!influxResults.series) { |
|
||||||
return []; |
|
||||||
} |
|
||||||
|
|
||||||
var series = influxResults.series[0]; |
|
||||||
return _.map(series.values, function(value) { |
|
||||||
if (_.isArray(value)) { |
|
||||||
return { text: value[0] }; |
|
||||||
} else { |
|
||||||
return { text: value }; |
|
||||||
} |
|
||||||
}); |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this._seriesQuery = function(query) { |
|
||||||
return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}); |
|
||||||
}; |
|
||||||
|
|
||||||
this.testDatasource = function() { |
|
||||||
return this.metricFindQuery('SHOW MEASUREMENTS LIMIT 1').then(function () { |
|
||||||
return { status: "success", message: "Data source is working", title: "Success" }; |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
this._influxRequest = function(method, url, data) { |
|
||||||
var self = this; |
|
||||||
|
|
||||||
var currentUrl = self.urls.shift(); |
|
||||||
self.urls.push(currentUrl); |
|
||||||
|
|
||||||
var params = { |
|
||||||
u: self.username, |
|
||||||
p: self.password, |
|
||||||
}; |
|
||||||
|
|
||||||
if (self.database) { |
|
||||||
params.db = self.database; |
|
||||||
} |
|
||||||
|
|
||||||
if (method === 'GET') { |
|
||||||
_.extend(params, data); |
|
||||||
data = null; |
|
||||||
} |
|
||||||
|
|
||||||
var options = { |
|
||||||
method: method, |
|
||||||
url: currentUrl + url, |
|
||||||
params: params, |
|
||||||
data: data, |
|
||||||
precision: "ms", |
|
||||||
inspect: { type: 'influxdb' }, |
|
||||||
}; |
|
||||||
|
|
||||||
options.headers = options.headers || {}; |
|
||||||
if (self.basicAuth) { |
|
||||||
options.headers.Authorization = self.basicAuth; |
|
||||||
} |
|
||||||
|
|
||||||
return backendSrv.datasourceRequest(options).then(function(result) { |
|
||||||
return result.data; |
|
||||||
}, function(err) { |
|
||||||
if (err.status !== 0 || err.status >= 300) { |
|
||||||
if (err.data && err.data.error) { |
|
||||||
throw { message: 'InfluxDB Error Response: ' + err.data.error, data: err.data, config: err.config }; |
|
||||||
} |
|
||||||
else { |
|
||||||
throw { message: 'InfluxDB Error: ' + err.message, data: err.data, config: err.config }; |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
function getTimeFilter(options) { |
|
||||||
var from = getInfluxTime(options.rangeRaw.from, false); |
|
||||||
var until = getInfluxTime(options.rangeRaw.to, true); |
|
||||||
var fromIsAbsolute = from[from.length-1] === 's'; |
|
||||||
|
|
||||||
if (until === 'now()' && !fromIsAbsolute) { |
|
||||||
return 'time > ' + from; |
|
||||||
} |
|
||||||
|
|
||||||
return 'time > ' + from + ' and time < ' + until; |
|
||||||
} |
|
||||||
|
|
||||||
function getInfluxTime(date, roundUp) { |
|
||||||
if (_.isString(date)) { |
|
||||||
if (date === 'now') { |
|
||||||
return 'now()'; |
|
||||||
} |
|
||||||
|
|
||||||
var parts = /^now-(\d+)([d|h|m|s])$/.exec(date); |
|
||||||
if (parts) { |
|
||||||
var amount = parseInt(parts[1]); |
|
||||||
var unit = parts[2]; |
|
||||||
return 'now() - ' + amount + unit; |
|
||||||
} |
|
||||||
date = dateMath.parse(date, roundUp); |
|
||||||
} |
|
||||||
return (date.valueOf() / 1000).toFixed(0) + 's'; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return InfluxDatasource; |
|
||||||
}); |
|
@ -0,0 +1,213 @@ |
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import angular from 'angular'; |
||||||
|
import _ from 'lodash'; |
||||||
|
|
||||||
|
import * as dateMath from 'app/core/utils/datemath'; |
||||||
|
import InfluxSeries from './influx_series'; |
||||||
|
import InfluxQuery from './influx_query'; |
||||||
|
|
||||||
|
/** @ngInject */ |
||||||
|
export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) { |
||||||
|
this.type = 'influxdb'; |
||||||
|
this.urls = _.map(instanceSettings.url.split(','), function(url) { |
||||||
|
return url.trim(); |
||||||
|
}); |
||||||
|
|
||||||
|
this.username = instanceSettings.username; |
||||||
|
this.password = instanceSettings.password; |
||||||
|
this.name = instanceSettings.name; |
||||||
|
this.database = instanceSettings.database; |
||||||
|
this.basicAuth = instanceSettings.basicAuth; |
||||||
|
|
||||||
|
this.supportAnnotations = true; |
||||||
|
this.supportMetrics = true; |
||||||
|
|
||||||
|
this.query = function(options) { |
||||||
|
var timeFilter = getTimeFilter(options); |
||||||
|
var queryTargets = []; |
||||||
|
var i, y; |
||||||
|
|
||||||
|
var allQueries = _.map(options.targets, function(target) { |
||||||
|
if (target.hide) { return []; } |
||||||
|
|
||||||
|
queryTargets.push(target); |
||||||
|
|
||||||
|
// build query
|
||||||
|
var queryModel = new InfluxQuery(target); |
||||||
|
var query = queryModel.render(); |
||||||
|
query = query.replace(/\$interval/g, (target.interval || options.interval)); |
||||||
|
return query; |
||||||
|
|
||||||
|
}).join("\n"); |
||||||
|
|
||||||
|
// replace grafana variables
|
||||||
|
allQueries = allQueries.replace(/\$timeFilter/g, timeFilter); |
||||||
|
|
||||||
|
// replace templated variables
|
||||||
|
allQueries = templateSrv.replace(allQueries, options.scopedVars); |
||||||
|
|
||||||
|
return this._seriesQuery(allQueries).then(function(data): any { |
||||||
|
if (!data || !data.results) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
var seriesList = []; |
||||||
|
for (i = 0; i < data.results.length; i++) { |
||||||
|
var result = data.results[i]; |
||||||
|
if (!result || !result.series) { continue; } |
||||||
|
|
||||||
|
var target = queryTargets[i]; |
||||||
|
var alias = target.alias; |
||||||
|
if (alias) { |
||||||
|
alias = templateSrv.replace(target.alias, options.scopedVars); |
||||||
|
} |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { data: seriesList }; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.annotationQuery = function(options) { |
||||||
|
var timeFilter = getTimeFilter({rangeRaw: options.rangeRaw}); |
||||||
|
var query = options.annotation.query.replace('$timeFilter', timeFilter); |
||||||
|
query = templateSrv.replace(query); |
||||||
|
|
||||||
|
return this._seriesQuery(query).then(function(data) { |
||||||
|
if (!data || !data.results || !data.results[0]) { |
||||||
|
throw { message: 'No results in response from InfluxDB' }; |
||||||
|
} |
||||||
|
return new InfluxSeries({series: data.results[0].series, annotation: options.annotation}).getAnnotations(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.metricFindQuery = function (query) { |
||||||
|
var interpolated; |
||||||
|
try { |
||||||
|
interpolated = templateSrv.replace(query); |
||||||
|
} catch (err) { |
||||||
|
return $q.reject(err); |
||||||
|
} |
||||||
|
|
||||||
|
return this._seriesQuery(interpolated).then(function (results) { |
||||||
|
if (!results || results.results.length === 0) { return []; } |
||||||
|
|
||||||
|
var influxResults = results.results[0]; |
||||||
|
if (!influxResults.series) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
var series = influxResults.series[0]; |
||||||
|
return _.map(series.values, function(value) { |
||||||
|
if (_.isArray(value)) { |
||||||
|
return { text: value[0] }; |
||||||
|
} else { |
||||||
|
return { text: value }; |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this._seriesQuery = function(query) { |
||||||
|
return this._influxRequest('GET', '/query', {q: query, epoch: 'ms'}); |
||||||
|
}; |
||||||
|
|
||||||
|
this.testDatasource = function() { |
||||||
|
return this.metricFindQuery('SHOW MEASUREMENTS LIMIT 1').then(function () { |
||||||
|
return { status: "success", message: "Data source is working", title: "Success" }; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
this._influxRequest = function(method, url, data) { |
||||||
|
var self = this; |
||||||
|
|
||||||
|
var currentUrl = self.urls.shift(); |
||||||
|
self.urls.push(currentUrl); |
||||||
|
|
||||||
|
var params: any = { |
||||||
|
u: self.username, |
||||||
|
p: self.password, |
||||||
|
}; |
||||||
|
|
||||||
|
if (self.database) { |
||||||
|
params.db = self.database; |
||||||
|
} |
||||||
|
|
||||||
|
if (method === 'GET') { |
||||||
|
_.extend(params, data); |
||||||
|
data = null; |
||||||
|
} |
||||||
|
|
||||||
|
var options: any = { |
||||||
|
method: method, |
||||||
|
url: currentUrl + url, |
||||||
|
params: params, |
||||||
|
data: data, |
||||||
|
precision: "ms", |
||||||
|
inspect: { type: 'influxdb' }, |
||||||
|
}; |
||||||
|
|
||||||
|
options.headers = options.headers || {}; |
||||||
|
if (self.basicAuth) { |
||||||
|
options.headers.Authorization = self.basicAuth; |
||||||
|
} |
||||||
|
|
||||||
|
return backendSrv.datasourceRequest(options).then(function(result) { |
||||||
|
return result.data; |
||||||
|
}, function(err) { |
||||||
|
if (err.status !== 0 || err.status >= 300) { |
||||||
|
if (err.data && err.data.error) { |
||||||
|
throw { message: 'InfluxDB Error Response: ' + err.data.error, data: err.data, config: err.config }; |
||||||
|
} else { |
||||||
|
throw { message: 'InfluxDB Error: ' + err.message, data: err.data, config: err.config }; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
function getTimeFilter(options) { |
||||||
|
var from = getInfluxTime(options.rangeRaw.from, false); |
||||||
|
var until = getInfluxTime(options.rangeRaw.to, true); |
||||||
|
var fromIsAbsolute = from[from.length-1] === 's'; |
||||||
|
|
||||||
|
if (until === 'now()' && !fromIsAbsolute) { |
||||||
|
return 'time > ' + from; |
||||||
|
} |
||||||
|
|
||||||
|
return 'time > ' + from + ' and time < ' + until; |
||||||
|
} |
||||||
|
|
||||||
|
function getInfluxTime(date, roundUp) { |
||||||
|
if (_.isString(date)) { |
||||||
|
if (date === 'now') { |
||||||
|
return 'now()'; |
||||||
|
} |
||||||
|
|
||||||
|
var parts = /^now-(\d+)([d|h|m|s])$/.exec(date); |
||||||
|
if (parts) { |
||||||
|
var amount = parseInt(parts[1]); |
||||||
|
var unit = parts[2]; |
||||||
|
return 'now() - ' + amount + unit; |
||||||
|
} |
||||||
|
date = dateMath.parse(date, roundUp); |
||||||
|
} |
||||||
|
return (date.valueOf() / 1000).toFixed(0) + 's'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,30 +0,0 @@ |
|||||||
define([ |
|
||||||
'./datasource', |
|
||||||
], |
|
||||||
function (InfluxDatasource) { |
|
||||||
'use strict'; |
|
||||||
|
|
||||||
function influxMetricsQueryEditor() { |
|
||||||
return {controller: 'InfluxQueryCtrl', templateUrl: 'public/app/plugins/datasource/influxdb/partials/query.editor.html'}; |
|
||||||
} |
|
||||||
|
|
||||||
function influxMetricsQueryOptions() { |
|
||||||
return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/query.options.html'}; |
|
||||||
} |
|
||||||
|
|
||||||
function influxAnnotationsQueryEditor() { |
|
||||||
return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/annotations.editor.html'}; |
|
||||||
} |
|
||||||
|
|
||||||
function influxConfigView() { |
|
||||||
return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/config.html'}; |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
Datasource: InfluxDatasource, |
|
||||||
metricsQueryEditor: influxMetricsQueryEditor, |
|
||||||
metricsQueryOptions: influxMetricsQueryOptions, |
|
||||||
annotationsQueryEditor: influxAnnotationsQueryEditor, |
|
||||||
configView: influxConfigView, |
|
||||||
}; |
|
||||||
}); |
|
@ -0,0 +1,53 @@ |
|||||||
|
import {InfluxDatasource} from './datasource'; |
||||||
|
import {InfluxQueryCtrl} from './query_ctrl'; |
||||||
|
|
||||||
|
class InfluxConfigCtrl { |
||||||
|
static templateUrl = 'public/app/plugins/datasource/influxdb/partials/config.html'; |
||||||
|
} |
||||||
|
|
||||||
|
class InfluxQueryOptionsCtrl { |
||||||
|
static templateUrl = 'public/app/plugins/datasource/influxdb/partials/query.options.html'; |
||||||
|
} |
||||||
|
|
||||||
|
class InfluxAnnotationsQueryCtrl { |
||||||
|
static templateUrl = 'public/app/plugins/datasource/influxdb/partials/annotations.editor.html'; |
||||||
|
} |
||||||
|
|
||||||
|
export { |
||||||
|
InfluxDatasource as Datasource, |
||||||
|
InfluxQueryCtrl as QueryCtrl, |
||||||
|
InfluxConfigCtrl as ConfigCtrl, |
||||||
|
InfluxQueryOptionsCtrl as QueryOptionsCtrl, |
||||||
|
InfluxAnnotationsQueryCtrl as AnnotationsQueryCtrl, |
||||||
|
}; |
||||||
|
|
||||||
|
// define([
|
||||||
|
// './datasource',
|
||||||
|
// ],
|
||||||
|
// function (InfluxDatasource) {
|
||||||
|
// 'use strict';
|
||||||
|
//
|
||||||
|
// function influxMetricsQueryEditor() {
|
||||||
|
// return {controller: 'InfluxQueryCtrl', templateUrl: 'public/app/plugins/datasource/influxdb/partials/query.editor.html'};
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function influxMetricsQueryOptions() {
|
||||||
|
// return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/query.options.html'};
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function influxAnnotationsQueryEditor() {
|
||||||
|
// return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/annotations.editor.html'};
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// function influxConfigView() {
|
||||||
|
// return {templateUrl: 'public/app/plugins/datasource/influxdb/partials/config.html'};
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return {
|
||||||
|
// Datasource: InfluxDatasource,
|
||||||
|
// metricsQueryEditor: influxMetricsQueryEditor,
|
||||||
|
// metricsQueryOptions: influxMetricsQueryOptions,
|
||||||
|
// annotationsQueryEditor: influxAnnotationsQueryEditor,
|
||||||
|
// configView: influxConfigView,
|
||||||
|
// };
|
||||||
|
// });
|
@ -1,119 +1,73 @@ |
|||||||
<div class=""> |
<query-editor-row ctrl="ctrl"> |
||||||
<div class="tight-form"> |
<ul class="tight-form-list" ng-hide="ctrl.target.rawQuery"> |
||||||
<ul class="tight-form-list pull-right"> |
|
||||||
<li ng-show="parserError" class="tight-form-item"> |
|
||||||
<a bs-tooltip="parserError" style="color: rgb(229, 189, 28)" role="menuitem"> |
|
||||||
<i class="fa fa-warning"></i> |
|
||||||
</a> |
|
||||||
</li> |
|
||||||
<li class="tight-form-item small" ng-show="target.datasource"> |
|
||||||
<em>{{target.datasource}}</em> |
|
||||||
</li> |
|
||||||
<li class="tight-form-item"> |
|
||||||
<div class="dropdown"> |
|
||||||
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1"> |
|
||||||
<i class="fa fa-bars"></i> |
|
||||||
</a> |
|
||||||
<ul class="dropdown-menu pull-right" role="menu"> |
|
||||||
<li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li> |
|
||||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.duplicateDataQuery(target)">Duplicate</a></li> |
|
||||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index-1)">Move up</a></li> |
|
||||||
<li role="menuitem"><a tabindex="1" ng-click="panelCtrl.moveDataQuery($index, $index+1)">Move down</a></li> |
|
||||||
</ul> |
|
||||||
</div> |
|
||||||
</li> |
|
||||||
|
|
||||||
<li class="tight-form-item last"> |
|
||||||
<a class="pointer" tabindex="1" ng-click="panelCtrl.removeDataQuery(target)"> |
|
||||||
<i class="fa fa-remove"></i> |
|
||||||
</a> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
|
|
||||||
<ul class="tight-form-list"> |
|
||||||
<li class="tight-form-item" style="min-width: 15px; text-align: center"> |
|
||||||
{{target.refId}} |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
<a class="tight-form-item" ng-click="target.hide = !target.hide; panelCtrl.refresh();" role="menuitem"> |
|
||||||
<i class="fa fa-eye"></i> |
|
||||||
</a> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
|
|
||||||
<ul class="tight-form-list" ng-hide="target.rawQuery"> |
|
||||||
<li class="tight-form-item query-keyword" style="width: 75px"> |
<li class="tight-form-item query-keyword" style="width: 75px"> |
||||||
FROM |
FROM |
||||||
</li> |
</li> |
||||||
<li> |
<li> |
||||||
<metric-segment segment="policySegment" get-options="getPolicySegments()" on-change="policyChanged()"></metric-segment> |
<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment> |
||||||
</li> |
</li> |
||||||
<li> |
<li> |
||||||
<metric-segment segment="measurementSegment" get-options="getMeasurements()" on-change="measurementChanged()"></metric-segment> |
<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements()" on-change="ctrl.measurementChanged()"></metric-segment> |
||||||
</li> |
</li> |
||||||
<li class="tight-form-item query-keyword" style="padding-left: 15px; padding-right: 15px;"> |
<li class="tight-form-item query-keyword" style="padding-left: 15px; padding-right: 15px;"> |
||||||
WHERE |
WHERE |
||||||
</li> |
</li> |
||||||
<li ng-repeat="segment in tagSegments"> |
<li ng-repeat="segment in ctrl.tagSegments"> |
||||||
<metric-segment segment="segment" get-options="getTagsOrValues(segment, $index)" on-change="tagSegmentUpdated(segment, $index)"></metric-segment> |
<metric-segment segment="segment" get-options="ctrl.getTagsOrValues(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment> |
||||||
</li> |
</li> |
||||||
</ul> |
</ul> |
||||||
|
|
||||||
<div class="tight-form-flex-wrapper" ng-show="target.rawQuery"> |
<div class="tight-form-flex-wrapper" ng-show="target.rawQuery"> |
||||||
<input type="text" class="tight-form-clear-input" ng-model="target.query" spellcheck="false" style="width: 100%;" ng-blur="panelCtrl.refresh()"></input> |
<input type="text" class="tight-form-clear-input" ng-model="ctrl.target.query" spellcheck="false" style="width: 100%;" ng-blur="ctrl.refresh()"></input> |
||||||
</div> |
</div> |
||||||
|
</query-editor-row> |
||||||
|
|
||||||
|
<div ng-hide="ctrl.target.rawQuery"> |
||||||
|
<div class="tight-form" ng-repeat="selectParts in ctrl.queryModel.selectModels"> |
||||||
|
<ul class="tight-form-list"> |
||||||
|
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
||||||
|
<span ng-show="$index === 0">SELECT</span> |
||||||
|
</li> |
||||||
|
<li ng-repeat="part in selectParts"> |
||||||
|
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeSelectPart(selectParts, part)" part-updated="ctrl.selectPartUpdated(selectParts, part)" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor> |
||||||
|
</li> |
||||||
|
<li class="dropdown" dropdown-typeahead="ctrl.selectMenu" dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)"> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
<div class="clearfix"></div> |
<div class="clearfix"></div> |
||||||
</div> |
</div> |
||||||
|
|
||||||
<div ng-hide="target.rawQuery"> |
|
||||||
|
|
||||||
<div class="tight-form" ng-repeat="selectParts in queryModel.selectModels"> |
|
||||||
<ul class="tight-form-list"> |
|
||||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
|
||||||
<span ng-show="$index === 0">SELECT</span> |
|
||||||
</li> |
|
||||||
<li ng-repeat="part in selectParts"> |
|
||||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="removeSelectPart(selectParts, part)" part-updated="selectPartUpdated(selectParts, part)" get-options="getPartOptions(part)"></influx-query-part-editor> |
|
||||||
</li> |
|
||||||
<li class="dropdown" dropdown-typeahead="selectMenu" dropdown-typeahead-on-select="addSelectPart(selectParts, $item, $subItem)"> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
<div class="clearfix"></div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="tight-form"> |
|
||||||
<ul class="tight-form-list"> |
|
||||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
|
||||||
<span>GROUP BY</span> |
|
||||||
</li> |
|
||||||
<li ng-repeat="part in queryModel.groupByParts"> |
|
||||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="removeGroupByPart(part, $index)" part-updated="panelCtrl.refresh();" get-options="getPartOptions(part)"></influx-query-part-editor> |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
<metric-segment segment="groupBySegment" get-options="getGroupByOptions()" on-change="groupByAction(part, $index)"></metric-segment> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
<div class="clearfix"></div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<div class="tight-form"> |
<div class="tight-form"> |
||||||
<ul class="tight-form-list"> |
<ul class="tight-form-list"> |
||||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
||||||
ALIAS BY |
<span>GROUP BY</span> |
||||||
</li> |
</li> |
||||||
<li> |
<li ng-repeat="part in ctrl.queryModel.groupByParts"> |
||||||
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="panelCtrl.refresh()"> |
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor> |
||||||
</li> |
|
||||||
<li class="tight-form-item"> |
|
||||||
Format as |
|
||||||
</li> |
</li> |
||||||
<li> |
<li> |
||||||
<select class="input-small tight-form-input" style="width: 104px" ng-model="target.resultFormat" ng-options="f.value as f.text for f in resultFormats" ng-change="panelCtrl.refresh()"></select> |
<metric-segment segment="ctrl.groupBySegment" get-options="ctrl.getGroupByOptions()" on-change="ctrl.groupByAction(part, $index)"></metric-segment> |
||||||
</li> |
</li> |
||||||
</ul> |
</ul> |
||||||
<div class="clearfix"></div> |
<div class="clearfix"></div> |
||||||
</div> |
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="tight-form"> |
||||||
|
<ul class="tight-form-list"> |
||||||
|
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;"> |
||||||
|
ALIAS BY |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()"> |
||||||
|
</li> |
||||||
|
<li class="tight-form-item"> |
||||||
|
Format as |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<select class="input-small tight-form-input" style="width: 104px" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
<div class="clearfix"></div> |
||||||
</div> |
</div> |
||||||
|
|
||||||
|
@ -1,322 +0,0 @@ |
|||||||
define([ |
|
||||||
'angular', |
|
||||||
'lodash', |
|
||||||
'./query_builder', |
|
||||||
'./influx_query', |
|
||||||
'./query_part', |
|
||||||
'./query_part_editor', |
|
||||||
], |
|
||||||
function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) { |
|
||||||
'use strict'; |
|
||||||
|
|
||||||
var module = angular.module('grafana.controllers'); |
|
||||||
|
|
||||||
InfluxQuery = InfluxQuery.default; |
|
||||||
queryPart = queryPart.default; |
|
||||||
|
|
||||||
module.controller('InfluxQueryCtrl', function($scope, templateSrv, $q, uiSegmentSrv) { |
|
||||||
var panelCtrl = $scope.ctrl; |
|
||||||
var datasource = $scope.datasource; |
|
||||||
$scope.panelCtrl = panelCtrl; |
|
||||||
|
|
||||||
$scope.init = function() { |
|
||||||
if (!$scope.target) { return; } |
|
||||||
|
|
||||||
$scope.target = $scope.target; |
|
||||||
$scope.queryModel = new InfluxQuery($scope.target); |
|
||||||
$scope.queryBuilder = new InfluxQueryBuilder($scope.target, datasource.database); |
|
||||||
$scope.groupBySegment = uiSegmentSrv.newPlusButton(); |
|
||||||
$scope.resultFormats = [ |
|
||||||
{text: 'Time series', value: 'time_series'}, |
|
||||||
{text: 'Table', value: 'table'}, |
|
||||||
]; |
|
||||||
|
|
||||||
$scope.policySegment = uiSegmentSrv.newSegment($scope.target.policy); |
|
||||||
|
|
||||||
if (!$scope.target.measurement) { |
|
||||||
$scope.measurementSegment = uiSegmentSrv.newSelectMeasurement(); |
|
||||||
} else { |
|
||||||
$scope.measurementSegment = uiSegmentSrv.newSegment($scope.target.measurement); |
|
||||||
} |
|
||||||
|
|
||||||
$scope.tagSegments = []; |
|
||||||
_.each($scope.target.tags, function(tag) { |
|
||||||
if (!tag.operator) { |
|
||||||
if (/^\/.*\/$/.test(tag.value)) { |
|
||||||
tag.operator = "=~"; |
|
||||||
} else { |
|
||||||
tag.operator = '='; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (tag.condition) { |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newCondition(tag.condition)); |
|
||||||
} |
|
||||||
|
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newKey(tag.key)); |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newOperator(tag.operator)); |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newKeyValue(tag.value)); |
|
||||||
}); |
|
||||||
|
|
||||||
$scope.fixTagSegments(); |
|
||||||
$scope.buildSelectMenu(); |
|
||||||
$scope.removeTagFilterSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove tag filter --'}); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.buildSelectMenu = function() { |
|
||||||
var categories = queryPart.getCategories(); |
|
||||||
$scope.selectMenu = _.reduce(categories, function(memo, cat, key) { |
|
||||||
var menu = {text: key}; |
|
||||||
menu.submenu = _.map(cat, function(item) { |
|
||||||
return {text: item.type, value: item.type}; |
|
||||||
}); |
|
||||||
memo.push(menu); |
|
||||||
return memo; |
|
||||||
}, []); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getGroupByOptions = function() { |
|
||||||
var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS'); |
|
||||||
|
|
||||||
return datasource.metricFindQuery(query) |
|
||||||
.then(function(tags) { |
|
||||||
var options = []; |
|
||||||
if (!$scope.queryModel.hasFill()) { |
|
||||||
options.push(uiSegmentSrv.newSegment({value: 'fill(null)'})); |
|
||||||
} |
|
||||||
if (!$scope.queryModel.hasGroupByTime()) { |
|
||||||
options.push(uiSegmentSrv.newSegment({value: 'time($interval)'})); |
|
||||||
} |
|
||||||
_.each(tags, function(tag) { |
|
||||||
options.push(uiSegmentSrv.newSegment({value: 'tag(' + tag.text + ')'})); |
|
||||||
}); |
|
||||||
return options; |
|
||||||
}) |
|
||||||
.then(null, $scope.handleQueryError); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.groupByAction = function() { |
|
||||||
$scope.queryModel.addGroupBy($scope.groupBySegment.value); |
|
||||||
var plusButton = uiSegmentSrv.newPlusButton(); |
|
||||||
$scope.groupBySegment.value = plusButton.value; |
|
||||||
$scope.groupBySegment.html = plusButton.html; |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.removeGroupByPart = function(part, index) { |
|
||||||
$scope.queryModel.removeGroupByPart(part, index); |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.addSelectPart = function(selectParts, cat, subitem) { |
|
||||||
$scope.queryModel.addSelectPart(selectParts, subitem.value); |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.removeSelectPart = function(selectParts, part) { |
|
||||||
$scope.queryModel.removeSelectPart(selectParts, part); |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.selectPartUpdated = function() { |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.fixTagSegments = function() { |
|
||||||
var count = $scope.tagSegments.length; |
|
||||||
var lastSegment = $scope.tagSegments[Math.max(count-1, 0)]; |
|
||||||
|
|
||||||
if (!lastSegment || lastSegment.type !== 'plus-button') { |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newPlusButton()); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.measurementChanged = function() { |
|
||||||
$scope.target.measurement = $scope.measurementSegment.value; |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getPolicySegments = function() { |
|
||||||
var policiesQuery = $scope.queryBuilder.buildExploreQuery('RETENTION POLICIES'); |
|
||||||
return datasource.metricFindQuery(policiesQuery) |
|
||||||
.then($scope.transformToSegments(false)) |
|
||||||
.then(null, $scope.handleQueryError); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.policyChanged = function() { |
|
||||||
$scope.target.policy = $scope.policySegment.value; |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.toggleQueryMode = function () { |
|
||||||
$scope.target.rawQuery = !$scope.target.rawQuery; |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getMeasurements = function () { |
|
||||||
var query = $scope.queryBuilder.buildExploreQuery('MEASUREMENTS'); |
|
||||||
return datasource.metricFindQuery(query) |
|
||||||
.then($scope.transformToSegments(true), $scope.handleQueryError); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getPartOptions = function(part) { |
|
||||||
if (part.def.type === 'field') { |
|
||||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS'); |
|
||||||
return datasource.metricFindQuery(fieldsQuery) |
|
||||||
.then($scope.transformToSegments(true), $scope.handleQueryError); |
|
||||||
} |
|
||||||
if (part.def.type === 'tag') { |
|
||||||
var tagsQuery = $scope.queryBuilder.buildExploreQuery('TAG_KEYS'); |
|
||||||
return datasource.metricFindQuery(tagsQuery) |
|
||||||
.then($scope.transformToSegments(true), $scope.handleQueryError); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.handleQueryError = function(err) { |
|
||||||
$scope.parserError = err.message || 'Failed to issue metric query'; |
|
||||||
return []; |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.transformToSegments = function(addTemplateVars) { |
|
||||||
return function(results) { |
|
||||||
var segments = _.map(results, function(segment) { |
|
||||||
return uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable }); |
|
||||||
}); |
|
||||||
|
|
||||||
if (addTemplateVars) { |
|
||||||
_.each(templateSrv.variables, function(variable) { |
|
||||||
segments.unshift(uiSegmentSrv.newSegment({ type: 'template', value: '/$' + variable.name + '$/', expandable: true })); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
return segments; |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getTagsOrValues = function(segment, index) { |
|
||||||
if (segment.type === 'condition') { |
|
||||||
return $q.when([uiSegmentSrv.newSegment('AND'), uiSegmentSrv.newSegment('OR')]); |
|
||||||
} |
|
||||||
if (segment.type === 'operator') { |
|
||||||
var nextValue = $scope.tagSegments[index+1].value; |
|
||||||
if (/^\/.*\/$/.test(nextValue)) { |
|
||||||
return $q.when(uiSegmentSrv.newOperators(['=~', '!~'])); |
|
||||||
} else { |
|
||||||
return $q.when(uiSegmentSrv.newOperators(['=', '<>', '<', '>'])); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
var query, addTemplateVars; |
|
||||||
if (segment.type === 'key' || segment.type === 'plus-button') { |
|
||||||
query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS'); |
|
||||||
addTemplateVars = false; |
|
||||||
} else if (segment.type === 'value') { |
|
||||||
query = $scope.queryBuilder.buildExploreQuery('TAG_VALUES', $scope.tagSegments[index-2].value); |
|
||||||
addTemplateVars = true; |
|
||||||
} |
|
||||||
|
|
||||||
return datasource.metricFindQuery(query) |
|
||||||
.then($scope.transformToSegments(addTemplateVars)) |
|
||||||
.then(function(results) { |
|
||||||
if (segment.type === 'key') { |
|
||||||
results.splice(0, 0, angular.copy($scope.removeTagFilterSegment)); |
|
||||||
} |
|
||||||
return results; |
|
||||||
}) |
|
||||||
.then(null, $scope.handleQueryError); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getFieldSegments = function() { |
|
||||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS'); |
|
||||||
return datasource.metricFindQuery(fieldsQuery) |
|
||||||
.then($scope.transformToSegments(false)) |
|
||||||
.then(null, $scope.handleQueryError); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getTagOptions = function() { |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.setFill = function(fill) { |
|
||||||
$scope.target.fill = fill; |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.tagSegmentUpdated = function(segment, index) { |
|
||||||
$scope.tagSegments[index] = segment; |
|
||||||
|
|
||||||
// handle remove tag condition
|
|
||||||
if (segment.value === $scope.removeTagFilterSegment.value) { |
|
||||||
$scope.tagSegments.splice(index, 3); |
|
||||||
if ($scope.tagSegments.length === 0) { |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newPlusButton()); |
|
||||||
} else if ($scope.tagSegments.length > 2) { |
|
||||||
$scope.tagSegments.splice(Math.max(index-1, 0), 1); |
|
||||||
if ($scope.tagSegments[$scope.tagSegments.length-1].type !== 'plus-button') { |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newPlusButton()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
if (segment.type === 'plus-button') { |
|
||||||
if (index > 2) { |
|
||||||
$scope.tagSegments.splice(index, 0, uiSegmentSrv.newCondition('AND')); |
|
||||||
} |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newOperator('=')); |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newFake('select tag value', 'value', 'query-segment-value')); |
|
||||||
segment.type = 'key'; |
|
||||||
segment.cssClass = 'query-segment-key'; |
|
||||||
} |
|
||||||
|
|
||||||
if ((index+1) === $scope.tagSegments.length) { |
|
||||||
$scope.tagSegments.push(uiSegmentSrv.newPlusButton()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$scope.rebuildTargetTagConditions(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.rebuildTargetTagConditions = function() { |
|
||||||
var tags = []; |
|
||||||
var tagIndex = 0; |
|
||||||
var tagOperator = ""; |
|
||||||
_.each($scope.tagSegments, function(segment2, index) { |
|
||||||
if (segment2.type === 'key') { |
|
||||||
if (tags.length === 0) { |
|
||||||
tags.push({}); |
|
||||||
} |
|
||||||
tags[tagIndex].key = segment2.value; |
|
||||||
} |
|
||||||
else if (segment2.type === 'value') { |
|
||||||
tagOperator = $scope.getTagValueOperator(segment2.value, tags[tagIndex].operator); |
|
||||||
if (tagOperator) { |
|
||||||
$scope.tagSegments[index-1] = uiSegmentSrv.newOperator(tagOperator); |
|
||||||
tags[tagIndex].operator = tagOperator; |
|
||||||
} |
|
||||||
tags[tagIndex].value = segment2.value; |
|
||||||
} |
|
||||||
else if (segment2.type === 'condition') { |
|
||||||
tags.push({ condition: segment2.value }); |
|
||||||
tagIndex += 1; |
|
||||||
} |
|
||||||
else if (segment2.type === 'operator') { |
|
||||||
tags[tagIndex].operator = segment2.value; |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
$scope.target.tags = tags; |
|
||||||
panelCtrl.refresh(); |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.getTagValueOperator = function(tagValue, tagOperator) { |
|
||||||
if (tagOperator !== '=~' && tagOperator !== '!~' && /^\/.*\/$/.test(tagValue)) { |
|
||||||
return '=~'; |
|
||||||
} |
|
||||||
else if ((tagOperator === '=~' || tagOperator === '!~') && /^(?!\/.*\/$)/.test(tagValue)) { |
|
||||||
return '='; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
$scope.init(); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
@ -0,0 +1,318 @@ |
|||||||
|
///<reference path="../../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import './query_part_editor'; |
||||||
|
import './query_part_editor'; |
||||||
|
|
||||||
|
import angular from 'angular'; |
||||||
|
import _ from 'lodash'; |
||||||
|
import InfluxQueryBuilder from './query_builder'; |
||||||
|
import InfluxQuery from './influx_query'; |
||||||
|
import queryPart from './query_part'; |
||||||
|
import {QueryCtrl} from 'app/features/panel/panel'; |
||||||
|
|
||||||
|
export class InfluxQueryCtrl extends QueryCtrl { |
||||||
|
static templateUrl = 'public/app/plugins/datasource/influxdb/partials/query.editor.html'; |
||||||
|
|
||||||
|
queryModel: InfluxQuery; |
||||||
|
queryBuilder: any; |
||||||
|
groupBySegment: any; |
||||||
|
resultFormats: any[]; |
||||||
|
policySegment: any; |
||||||
|
tagSegments: any[]; |
||||||
|
selectMenu: any; |
||||||
|
measurementSegment: any; |
||||||
|
removeTagFilterSegment: any; |
||||||
|
|
||||||
|
constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) { |
||||||
|
super($scope, $injector); |
||||||
|
|
||||||
|
this.target = this.target; |
||||||
|
this.queryModel = new InfluxQuery(this.target); |
||||||
|
this.queryBuilder = new InfluxQueryBuilder(this.target, this.datasource.database); |
||||||
|
this.groupBySegment = this.uiSegmentSrv.newPlusButton(); |
||||||
|
this.resultFormats = [ |
||||||
|
{text: 'Time series', value: 'time_series'}, |
||||||
|
{text: 'Table', value: 'table'}, |
||||||
|
]; |
||||||
|
|
||||||
|
this.policySegment = uiSegmentSrv.newSegment(this.target.policy); |
||||||
|
|
||||||
|
if (!this.target.measurement) { |
||||||
|
this.measurementSegment = uiSegmentSrv.newSelectMeasurement(); |
||||||
|
} else { |
||||||
|
this.measurementSegment = uiSegmentSrv.newSegment(this.target.measurement); |
||||||
|
} |
||||||
|
|
||||||
|
this.tagSegments = []; |
||||||
|
for (let tag of this.target.tags) { |
||||||
|
if (!tag.operator) { |
||||||
|
if (/^\/.*\/$/.test(tag.value)) { |
||||||
|
tag.operator = "=~"; |
||||||
|
} else { |
||||||
|
tag.operator = '='; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (tag.condition) { |
||||||
|
this.tagSegments.push(uiSegmentSrv.newCondition(tag.condition)); |
||||||
|
} |
||||||
|
|
||||||
|
this.tagSegments.push(uiSegmentSrv.newKey(tag.key)); |
||||||
|
this.tagSegments.push(uiSegmentSrv.newOperator(tag.operator)); |
||||||
|
this.tagSegments.push(uiSegmentSrv.newKeyValue(tag.value)); |
||||||
|
} |
||||||
|
|
||||||
|
this.fixTagSegments(); |
||||||
|
this.buildSelectMenu(); |
||||||
|
this.removeTagFilterSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove tag filter --'}); |
||||||
|
} |
||||||
|
|
||||||
|
buildSelectMenu() { |
||||||
|
var categories = queryPart.getCategories(); |
||||||
|
this.selectMenu = _.reduce(categories, function(memo, cat, key) { |
||||||
|
var menu = { |
||||||
|
text: key, |
||||||
|
submenu: cat.map(item => { |
||||||
|
return {text: item.type, value: item.type}; |
||||||
|
}), |
||||||
|
}; |
||||||
|
memo.push(menu); |
||||||
|
return memo; |
||||||
|
}, []); |
||||||
|
} |
||||||
|
|
||||||
|
getGroupByOptions() { |
||||||
|
var query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); |
||||||
|
|
||||||
|
return this.datasource.metricFindQuery(query).then(tags => { |
||||||
|
var options = []; |
||||||
|
if (!this.queryModel.hasFill()) { |
||||||
|
options.push(this.uiSegmentSrv.newSegment({value: 'fill(null)'})); |
||||||
|
} |
||||||
|
if (!this.queryModel.hasGroupByTime()) { |
||||||
|
options.push(this.uiSegmentSrv.newSegment({value: 'time($interval)'})); |
||||||
|
} |
||||||
|
for (let tag of tags) { |
||||||
|
options.push(this.uiSegmentSrv.newSegment({value: 'tag(' + tag.text + ')'})); |
||||||
|
} |
||||||
|
return options; |
||||||
|
}).catch(this.handleQueryError.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
groupByAction() { |
||||||
|
this.queryModel.addGroupBy(this.groupBySegment.value); |
||||||
|
var plusButton = this.uiSegmentSrv.newPlusButton(); |
||||||
|
this.groupBySegment.value = plusButton.value; |
||||||
|
this.groupBySegment.html = plusButton.html; |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
removeGroupByPart(part, index) { |
||||||
|
this.queryModel.removeGroupByPart(part, index); |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
addSelectPart(selectParts, cat, subitem) { |
||||||
|
this.queryModel.addSelectPart(selectParts, subitem.value); |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
removeSelectPart(selectParts, part) { |
||||||
|
this.queryModel.removeSelectPart(selectParts, part); |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
selectPartUpdated() { |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
fixTagSegments() { |
||||||
|
var count = this.tagSegments.length; |
||||||
|
var lastSegment = this.tagSegments[Math.max(count-1, 0)]; |
||||||
|
|
||||||
|
if (!lastSegment || lastSegment.type !== 'plus-button') { |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
measurementChanged() { |
||||||
|
this.target.measurement = this.measurementSegment.value; |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
getPolicySegments() { |
||||||
|
var policiesQuery = this.queryBuilder.buildExploreQuery('RETENTION POLICIES'); |
||||||
|
return this.datasource.metricFindQuery(policiesQuery) |
||||||
|
.then(this.transformToSegments(false)) |
||||||
|
.catch(this.handleQueryError.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
policyChanged() { |
||||||
|
this.target.policy = this.policySegment.value; |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
toggleQueryMode() { |
||||||
|
this.target.rawQuery = !this.target.rawQuery; |
||||||
|
} |
||||||
|
|
||||||
|
getMeasurements() { |
||||||
|
var query = this.queryBuilder.buildExploreQuery('MEASUREMENTS'); |
||||||
|
return this.datasource.metricFindQuery(query) |
||||||
|
.then(this.transformToSegments(true)) |
||||||
|
.catch(this.handleQueryError.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
getPartOptions(part) { |
||||||
|
if (part.def.type === 'field') { |
||||||
|
var fieldsQuery = this.queryBuilder.buildExploreQuery('FIELDS'); |
||||||
|
return this.datasource.metricFindQuery(fieldsQuery) |
||||||
|
.then(this.transformToSegments(true)) |
||||||
|
.catch(this.handleQueryError.bind(this)); |
||||||
|
} |
||||||
|
if (part.def.type === 'tag') { |
||||||
|
var tagsQuery = this.queryBuilder.buildExploreQuery('TAG_KEYS'); |
||||||
|
return this.datasource.metricFindQuery(tagsQuery) |
||||||
|
.then(this.transformToSegments(true)) |
||||||
|
.catch(this.handleQueryError.bind(true)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
handleQueryError(err) { |
||||||
|
this.error = err.message || 'Failed to issue metric query'; |
||||||
|
return []; |
||||||
|
} |
||||||
|
|
||||||
|
transformToSegments(addTemplateVars) { |
||||||
|
return (results) => { |
||||||
|
var segments = _.map(results, segment => { |
||||||
|
return this.uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable }); |
||||||
|
}); |
||||||
|
|
||||||
|
if (addTemplateVars) { |
||||||
|
for (let variable of this.templateSrv.variables) { |
||||||
|
segments.unshift(this.uiSegmentSrv.newSegment({ type: 'template', value: '/$' + variable.name + '$/', expandable: true })); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return segments; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
getTagsOrValues(segment, index) { |
||||||
|
if (segment.type === 'condition') { |
||||||
|
return this.$q.when([this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR')]); |
||||||
|
} |
||||||
|
if (segment.type === 'operator') { |
||||||
|
var nextValue = this.tagSegments[index+1].value; |
||||||
|
if (/^\/.*\/$/.test(nextValue)) { |
||||||
|
return this.$q.when(this.uiSegmentSrv.newOperators(['=~', '!~'])); |
||||||
|
} else { |
||||||
|
return this.$q.when(this.uiSegmentSrv.newOperators(['=', '<>', '<', '>'])); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
var query, addTemplateVars; |
||||||
|
if (segment.type === 'key' || segment.type === 'plus-button') { |
||||||
|
query = this.queryBuilder.buildExploreQuery('TAG_KEYS'); |
||||||
|
addTemplateVars = false; |
||||||
|
} else if (segment.type === 'value') { |
||||||
|
query = this.queryBuilder.buildExploreQuery('TAG_VALUES', this.tagSegments[index-2].value); |
||||||
|
addTemplateVars = true; |
||||||
|
} |
||||||
|
|
||||||
|
return this.datasource.metricFindQuery(query) |
||||||
|
.then(this.transformToSegments(addTemplateVars)) |
||||||
|
.then(results => { |
||||||
|
if (segment.type === 'key') { |
||||||
|
results.splice(0, 0, angular.copy(this.removeTagFilterSegment)); |
||||||
|
} |
||||||
|
return results; |
||||||
|
}) |
||||||
|
.catch(this.handleQueryError.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
getFieldSegments() { |
||||||
|
var fieldsQuery = this.queryBuilder.buildExploreQuery('FIELDS'); |
||||||
|
return this.datasource.metricFindQuery(fieldsQuery) |
||||||
|
.then(this.transformToSegments(false)) |
||||||
|
.catch(this.handleQueryError); |
||||||
|
} |
||||||
|
|
||||||
|
setFill(fill) { |
||||||
|
this.target.fill = fill; |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
tagSegmentUpdated(segment, index) { |
||||||
|
this.tagSegments[index] = segment; |
||||||
|
|
||||||
|
// handle remove tag condition
|
||||||
|
if (segment.value === this.removeTagFilterSegment.value) { |
||||||
|
this.tagSegments.splice(index, 3); |
||||||
|
if (this.tagSegments.length === 0) { |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); |
||||||
|
} else if (this.tagSegments.length > 2) { |
||||||
|
this.tagSegments.splice(Math.max(index-1, 0), 1); |
||||||
|
if (this.tagSegments[this.tagSegments.length-1].type !== 'plus-button') { |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (segment.type === 'plus-button') { |
||||||
|
if (index > 2) { |
||||||
|
this.tagSegments.splice(index, 0, this.uiSegmentSrv.newCondition('AND')); |
||||||
|
} |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newOperator('=')); |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newFake('select tag value', 'value', 'query-segment-value')); |
||||||
|
segment.type = 'key'; |
||||||
|
segment.cssClass = 'query-segment-key'; |
||||||
|
} |
||||||
|
|
||||||
|
if ((index+1) === this.tagSegments.length) { |
||||||
|
this.tagSegments.push(this.uiSegmentSrv.newPlusButton()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
this.rebuildTargetTagConditions(); |
||||||
|
} |
||||||
|
|
||||||
|
rebuildTargetTagConditions() { |
||||||
|
var tags = []; |
||||||
|
var tagIndex = 0; |
||||||
|
var tagOperator = ""; |
||||||
|
|
||||||
|
_.each(this.tagSegments, (segment2, index) => { |
||||||
|
if (segment2.type === 'key') { |
||||||
|
if (tags.length === 0) { |
||||||
|
tags.push({}); |
||||||
|
} |
||||||
|
tags[tagIndex].key = segment2.value; |
||||||
|
} else if (segment2.type === 'value') { |
||||||
|
tagOperator = this.getTagValueOperator(segment2.value, tags[tagIndex].operator); |
||||||
|
if (tagOperator) { |
||||||
|
this.tagSegments[index-1] = this.uiSegmentSrv.newOperator(tagOperator); |
||||||
|
tags[tagIndex].operator = tagOperator; |
||||||
|
} |
||||||
|
tags[tagIndex].value = segment2.value; |
||||||
|
} else if (segment2.type === 'condition') { |
||||||
|
tags.push({ condition: segment2.value }); |
||||||
|
tagIndex += 1; |
||||||
|
} else if (segment2.type === 'operator') { |
||||||
|
tags[tagIndex].operator = segment2.value; |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
this.target.tags = tags; |
||||||
|
this.panelCtrl.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
getTagValueOperator(tagValue, tagOperator) { |
||||||
|
if (tagOperator !== '=~' && tagOperator !== '!~' && /^\/.*\/$/.test(tagValue)) { |
||||||
|
return '=~'; |
||||||
|
} else if ((tagOperator === '=~' || tagOperator === '!~') && /^(?!\/.*\/$)/.test(tagValue)) { |
||||||
|
return '='; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue