Commit 6e429b85 by Torkel Ödegaard

feat(adhoc filters): good progress on ad hoc filters and sync from to url, #6038

parent cb522d58
...@@ -21,7 +21,7 @@ export class AdHocFiltersCtrl { ...@@ -21,7 +21,7 @@ export class AdHocFiltersCtrl {
if (this.variable.value && !_.isArray(this.variable.value)) { if (this.variable.value && !_.isArray(this.variable.value)) {
} }
for (let tag of this.variable.tags) { for (let tag of this.variable.filters) {
if (this.segments.length > 0) { if (this.segments.length > 0) {
this.segments.push(this.uiSegmentSrv.newCondition('AND')); this.segments.push(this.uiSegmentSrv.newCondition('AND'));
} }
...@@ -38,7 +38,11 @@ export class AdHocFiltersCtrl { ...@@ -38,7 +38,11 @@ export class AdHocFiltersCtrl {
getOptions(segment, index) { getOptions(segment, index) {
if (segment.type === 'operator') { if (segment.type === 'operator') {
return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<>', '<', '>', '=~', '!~'])); return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<', '>', '=~', '!~']));
}
if (segment.type === 'condition') {
return this.$q.when([this.uiSegmentSrv.newSegment('AND')]);
} }
return this.datasourceSrv.get(this.variable.datasource).then(ds => { return this.datasourceSrv.get(this.variable.datasource).then(ds => {
...@@ -100,37 +104,45 @@ export class AdHocFiltersCtrl { ...@@ -100,37 +104,45 @@ export class AdHocFiltersCtrl {
} }
updateVariableModel() { updateVariableModel() {
var tags = []; var filters = [];
var tagIndex = -1; var filterIndex = -1;
var tagOperator = ""; var operator = "";
var hasFakes = false;
this.segments.forEach((segment, index) => {
if (segment.fake) { this.segments.forEach(segment => {
if (segment.type === 'value' && segment.fake) {
hasFakes = true;
return; return;
} }
switch (segment.type) { switch (segment.type) {
case 'key': { case 'key': {
tags.push({key: segment.value}); filters.push({key: segment.value});
tagIndex += 1; filterIndex += 1;
break; break;
} }
case 'value': { case 'value': {
tags[tagIndex].value = segment.value; filters[filterIndex].value = segment.value;
break; break;
} }
case 'operator': { case 'operator': {
tags[tagIndex].operator = segment.value; filters[filterIndex].operator = segment.value;
break; break;
} }
case 'condition': { case 'condition': {
filters[filterIndex].condition = segment.value;
break; break;
} }
} }
}); });
if (hasFakes) {
return;
}
this.variable.setFilters(filters);
this.$rootScope.$emit('template-variable-value-updated');
this.$rootScope.$broadcast('refresh'); this.$rootScope.$broadcast('refresh');
this.variable.tags = tags;
} }
} }
......
...@@ -34,10 +34,6 @@ function (angular, _, $) { ...@@ -34,10 +34,6 @@ function (angular, _, $) {
$location.search(urlParams); $location.search(urlParams);
}); });
$scope.onAppEvent('template-variable-value-updated', function() {
self.updateUrlParamsWithCurrentVariables();
});
$scope.onAppEvent('$routeUpdate', function() { $scope.onAppEvent('$routeUpdate', function() {
var urlState = self.getQueryStringState(); var urlState = self.getQueryStringState();
if (self.needsSync(urlState)) { if (self.needsSync(urlState)) {
...@@ -57,22 +53,6 @@ function (angular, _, $) { ...@@ -57,22 +53,6 @@ function (angular, _, $) {
this.expandRowForPanel(); this.expandRowForPanel();
} }
DashboardViewState.prototype.updateUrlParamsWithCurrentVariables = function() {
// update url
var params = $location.search();
// remove variable params
_.each(params, function(value, key) {
if (key.indexOf('var-') === 0) {
delete params[key];
}
});
// add new values
templateSrv.fillVariableValuesForUrl(params);
// update url
$location.search(params);
};
DashboardViewState.prototype.expandRowForPanel = function() { DashboardViewState.prototype.expandRowForPanel = function() {
if (!this.state.panelId) { return; } if (!this.state.panelId) { return; }
......
...@@ -6,6 +6,7 @@ import {Variable, assignModelProperties, variableTypes} from './variable'; ...@@ -6,6 +6,7 @@ import {Variable, assignModelProperties, variableTypes} from './variable';
import {VariableSrv} from './variable_srv'; import {VariableSrv} from './variable_srv';
export class AdhocVariable implements Variable { export class AdhocVariable implements Variable {
filters: any[];
defaults = { defaults = {
type: 'adhoc', type: 'adhoc',
...@@ -13,9 +14,7 @@ export class AdhocVariable implements Variable { ...@@ -13,9 +14,7 @@ export class AdhocVariable implements Variable {
label: '', label: '',
hide: 0, hide: 0,
datasource: null, datasource: null,
options: [], filters: [],
current: {},
tags: {},
}; };
/** @ngInject **/ /** @ngInject **/
...@@ -41,8 +40,31 @@ export class AdhocVariable implements Variable { ...@@ -41,8 +40,31 @@ export class AdhocVariable implements Variable {
} }
setValueFromUrl(urlValue) { setValueFromUrl(urlValue) {
if (!_.isArray(urlValue)) {
urlValue = [urlValue];
}
this.filters = urlValue.map(item => {
var values = item.split('|');
return {
key: values[0],
operator: values[1],
value: values[2],
};
});
return Promise.resolve(); return Promise.resolve();
} }
getValueForUrl() {
return this.filters.map(filter => {
return filter.key + '|' + filter.operator + '|' + filter.value;
});
}
setFilters(filters: any[]) {
this.filters = filters;
}
} }
variableTypes['adhoc'] = { variableTypes['adhoc'] = {
......
...@@ -7,6 +7,7 @@ import {VariableSrv} from './variable_srv'; ...@@ -7,6 +7,7 @@ import {VariableSrv} from './variable_srv';
export class ConstantVariable implements Variable { export class ConstantVariable implements Variable {
query: string; query: string;
options: any[]; options: any[];
current: any;
defaults = { defaults = {
type: 'constant', type: 'constant',
...@@ -14,6 +15,7 @@ export class ConstantVariable implements Variable { ...@@ -14,6 +15,7 @@ export class ConstantVariable implements Variable {
hide: 2, hide: 2,
label: '', label: '',
query: '', query: '',
current: {},
}; };
/** @ngInject */ /** @ngInject */
...@@ -43,6 +45,11 @@ export class ConstantVariable implements Variable { ...@@ -43,6 +45,11 @@ export class ConstantVariable implements Variable {
setValueFromUrl(urlValue) { setValueFromUrl(urlValue) {
return this.variableSrv.setOptionFromUrl(this, urlValue); return this.variableSrv.setOptionFromUrl(this, urlValue);
} }
getValueForUrl() {
return this.current.value;
}
} }
variableTypes['constant'] = { variableTypes['constant'] = {
......
...@@ -10,6 +10,7 @@ export class CustomVariable implements Variable { ...@@ -10,6 +10,7 @@ export class CustomVariable implements Variable {
options: any; options: any;
includeAll: boolean; includeAll: boolean;
multi: boolean; multi: boolean;
current: any;
defaults = { defaults = {
type: 'custom', type: 'custom',
...@@ -17,10 +18,11 @@ export class CustomVariable implements Variable { ...@@ -17,10 +18,11 @@ export class CustomVariable implements Variable {
label: '', label: '',
hide: 0, hide: 0,
options: [], options: [],
current: {text: '', value: ''}, current: {},
query: '', query: '',
includeAll: false, includeAll: false,
multi: false, multi: false,
allValue: null,
}; };
/** @ngInject **/ /** @ngInject **/
...@@ -61,6 +63,13 @@ export class CustomVariable implements Variable { ...@@ -61,6 +63,13 @@ export class CustomVariable implements Variable {
setValueFromUrl(urlValue) { setValueFromUrl(urlValue) {
return this.variableSrv.setOptionFromUrl(this, urlValue); return this.variableSrv.setOptionFromUrl(this, urlValue);
} }
getValueForUrl() {
if (this.current.text === 'All') {
return 'All';
}
return this.current.value;
}
} }
variableTypes['custom'] = { variableTypes['custom'] = {
......
...@@ -9,13 +9,14 @@ export class DatasourceVariable implements Variable { ...@@ -9,13 +9,14 @@ export class DatasourceVariable implements Variable {
regex: any; regex: any;
query: string; query: string;
options: any; options: any;
current: any;
defaults = { defaults = {
type: 'datasource', type: 'datasource',
name: '', name: '',
hide: 0, hide: 0,
label: '', label: '',
current: {text: '', value: ''}, current: {},
regex: '', regex: '',
options: [], options: [],
query: '', query: '',
...@@ -73,6 +74,10 @@ export class DatasourceVariable implements Variable { ...@@ -73,6 +74,10 @@ export class DatasourceVariable implements Variable {
setValueFromUrl(urlValue) { setValueFromUrl(urlValue) {
return this.variableSrv.setOptionFromUrl(this, urlValue); return this.variableSrv.setOptionFromUrl(this, urlValue);
} }
getValueForUrl() {
return this.current.value;
}
} }
variableTypes['datasource'] = { variableTypes['datasource'] = {
......
...@@ -12,6 +12,7 @@ export class IntervalVariable implements Variable { ...@@ -12,6 +12,7 @@ export class IntervalVariable implements Variable {
auto: boolean; auto: boolean;
query: string; query: string;
refresh: number; refresh: number;
current: any;
defaults = { defaults = {
type: 'interval', type: 'interval',
...@@ -20,7 +21,7 @@ export class IntervalVariable implements Variable { ...@@ -20,7 +21,7 @@ export class IntervalVariable implements Variable {
label: '', label: '',
refresh: 2, refresh: 2,
options: [], options: [],
current: {text: '', value: ''}, current: {},
query: '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d', query: '1m,10m,30m,1h,6h,12h,1d,7d,14d,30d',
auto: false, auto: false,
auto_min: '10s', auto_min: '10s',
...@@ -75,6 +76,10 @@ export class IntervalVariable implements Variable { ...@@ -75,6 +76,10 @@ export class IntervalVariable implements Variable {
this.updateAutoValue(); this.updateAutoValue();
return this.variableSrv.setOptionFromUrl(this, urlValue); return this.variableSrv.setOptionFromUrl(this, urlValue);
} }
getValueForUrl() {
return this.current.value;
}
} }
variableTypes['interval'] = { variableTypes['interval'] = {
......
...@@ -33,8 +33,11 @@ export class QueryVariable implements Variable { ...@@ -33,8 +33,11 @@ export class QueryVariable implements Variable {
name: '', name: '',
multi: false, multi: false,
includeAll: false, includeAll: false,
allValue: null,
options: [], options: [],
current: {text: '', value: ''}, current: {},
tagsQuery: null,
tagValuesQuery: null,
}; };
constructor(private model, private datasourceSrv, private templateSrv, private variableSrv, private $q) { constructor(private model, private datasourceSrv, private templateSrv, private variableSrv, private $q) {
...@@ -56,6 +59,13 @@ export class QueryVariable implements Variable { ...@@ -56,6 +59,13 @@ export class QueryVariable implements Variable {
return this.variableSrv.setOptionFromUrl(this, urlValue); return this.variableSrv.setOptionFromUrl(this, urlValue);
} }
getValueForUrl() {
if (this.current.text === 'All') {
return 'All';
}
return this.current.value;
}
updateOptions() { updateOptions() {
return this.datasourceSrv.get(this.datasource) return this.datasourceSrv.get(this.datasource)
.then(this.updateOptionsFromMetricFindQuery.bind(this)) .then(this.updateOptionsFromMetricFindQuery.bind(this))
......
define([ import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
'../mocks/dashboard-mock',
'lodash', import '../all';
'app/features/templating/templateSrv' import {Emitter} from 'app/core/core';
], function(dashboardMock) {
'use strict'; describe('templateSrv', function() {
var _templateSrv, _variableSrv;
describe('templateSrv', function() {
var _templateSrv;
var _dashboard;
beforeEach(module('grafana.services'));
beforeEach(module(function() {
_dashboard = dashboardMock.create();
}));
beforeEach(inject(function(templateSrv) { beforeEach(angularMocks.module('grafana.core'));
beforeEach(angularMocks.module('grafana.services'));
beforeEach(angularMocks.inject(function(variableSrv, templateSrv) {
_templateSrv = templateSrv; _templateSrv = templateSrv;
_variableSrv = variableSrv;
})); }));
function initTemplateSrv(variables) {
_variableSrv.init({
templating: {list: variables},
events: new Emitter(),
});
}
describe('init', function() { describe('init', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]); initTemplateSrv([{type: 'query', name: 'test', current: {value: 'oogle'}}]);
}); });
it('should initialize template data', function() { it('should initialize template data', function() {
...@@ -31,7 +34,7 @@ define([ ...@@ -31,7 +34,7 @@ define([
describe('replace can pass scoped vars', function() { describe('replace can pass scoped vars', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]); initTemplateSrv([{type: 'query', name: 'test', current: {value: 'oogle' }}]);
}); });
it('should replace $test with scoped value', function() { it('should replace $test with scoped value', function() {
...@@ -47,7 +50,7 @@ define([ ...@@ -47,7 +50,7 @@ define([
describe('replace can pass multi / all format', function() { describe('replace can pass multi / all format', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{name: 'test', current: {value: ['value1', 'value2'] }}]); initTemplateSrv([{type: 'query', name: 'test', current: {value: ['value1', 'value2'] }}]);
}); });
it('should replace $test with globbed value', function() { it('should replace $test with globbed value', function() {
...@@ -68,7 +71,8 @@ define([ ...@@ -68,7 +71,8 @@ define([
describe('variable with all option', function() { describe('variable with all option', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ initTemplateSrv([{
type: 'query',
name: 'test', name: 'test',
current: {value: '$__all' }, current: {value: '$__all' },
options: [ options: [
...@@ -85,7 +89,8 @@ define([ ...@@ -85,7 +89,8 @@ define([
describe('variable with all option and custom value', function() { describe('variable with all option and custom value', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ initTemplateSrv([{
type: 'query',
name: 'test', name: 'test',
current: {value: '$__all' }, current: {value: '$__all' },
allValue: '*', allValue: '*',
...@@ -108,7 +113,7 @@ define([ ...@@ -108,7 +113,7 @@ define([
describe('lucene format', function() { describe('lucene format', function() {
it('should properly escape $test with lucene escape sequences', function() { it('should properly escape $test with lucene escape sequences', function() {
_templateSrv.init([{name: 'test', current: {value: 'value/4' }}]); initTemplateSrv([{type: 'query', name: 'test', current: {value: 'value/4' }}]);
var target = _templateSrv.replace('this:$test', {}, 'lucene'); var target = _templateSrv.replace('this:$test', {}, 'lucene');
expect(target).to.be("this:value\\\/4"); expect(target).to.be("this:value\\\/4");
}); });
...@@ -149,7 +154,7 @@ define([ ...@@ -149,7 +154,7 @@ define([
describe('can check if variable exists', function() { describe('can check if variable exists', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]); initTemplateSrv([{type: 'query', name: 'test', current: { value: 'oogle' } }]);
}); });
it('should return true if exists', function() { it('should return true if exists', function() {
...@@ -160,7 +165,7 @@ define([ ...@@ -160,7 +165,7 @@ define([
describe('can hightlight variables in string', function() { describe('can hightlight variables in string', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: 'oogle' } }]); initTemplateSrv([{type: 'query', name: 'test', current: { value: 'oogle' } }]);
}); });
it('should insert html', function() { it('should insert html', function() {
...@@ -181,7 +186,7 @@ define([ ...@@ -181,7 +186,7 @@ define([
describe('updateTemplateData with simple value', function() { describe('updateTemplateData with simple value', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: 'muuuu' } }]); initTemplateSrv([{type: 'query', name: 'test', current: { value: 'muuuu' } }]);
}); });
it('should set current value and update template data', function() { it('should set current value and update template data', function() {
...@@ -192,7 +197,7 @@ define([ ...@@ -192,7 +197,7 @@ define([
describe('fillVariableValuesForUrl with multi value', function() { describe('fillVariableValuesForUrl with multi value', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: ['val1', 'val2'] }}]); initTemplateSrv([{type: 'query', name: 'test', current: { value: ['val1', 'val2'] }}]);
}); });
it('should set multiple url params', function() { it('should set multiple url params', function() {
...@@ -204,10 +209,10 @@ define([ ...@@ -204,10 +209,10 @@ define([
describe('fillVariableValuesForUrl with multi value and scopedVars', function() { describe('fillVariableValuesForUrl with multi value and scopedVars', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([{ name: 'test', current: { value: ['val1', 'val2'] }}]); initTemplateSrv([{type: 'query', name: 'test', current: { value: ['val1', 'val2'] }}]);
}); });
it('should set multiple url params', function() { it('should set scoped value as url params', function() {
var params = {}; var params = {};
_templateSrv.fillVariableValuesForUrl(params, {'test': {value: 'val1'}}); _templateSrv.fillVariableValuesForUrl(params, {'test': {value: 'val1'}});
expect(params['var-test']).to.eql('val1'); expect(params['var-test']).to.eql('val1');
...@@ -216,9 +221,9 @@ define([ ...@@ -216,9 +221,9 @@ define([
describe('replaceWithText', function() { describe('replaceWithText', function() {
beforeEach(function() { beforeEach(function() {
_templateSrv.init([ initTemplateSrv([
{ name: 'server', current: { value: '{asd,asd2}', text: 'All' } }, {type: 'query', name: 'server', current: { value: '{asd,asd2}', text: 'All' } },
{ name: 'period', current: { value: '$__auto_interval', text: 'auto' } } {type: 'interval', name: 'period', current: { value: '$__auto_interval', text: 'auto' } }
]); ]);
_templateSrv.setGrafanaVariable('$__auto_interval', '13m'); _templateSrv.setGrafanaVariable('$__auto_interval', '13m');
_templateSrv.updateTemplateData(); _templateSrv.updateTemplateData();
...@@ -229,7 +234,4 @@ define([ ...@@ -229,7 +234,4 @@ define([
expect(target).to.be('Server: All, period: 13m'); expect(target).to.be('Server: All, period: 13m');
}); });
}); });
});
}); });
...@@ -180,18 +180,11 @@ function (angular, _, kbn) { ...@@ -180,18 +180,11 @@ function (angular, _, kbn) {
this.fillVariableValuesForUrl = function(params, scopedVars) { this.fillVariableValuesForUrl = function(params, scopedVars) {
_.each(this.variables, function(variable) { _.each(this.variables, function(variable) {
var current = variable.current;
var value = current.value;
if (current.text === 'All') {
value = 'All';
}
if (scopedVars && scopedVars[variable.name] !== void 0) { if (scopedVars && scopedVars[variable.name] !== void 0) {
value = scopedVars[variable.name].value; params['var-' + variable.name] = scopedVars[variable.name].value;
} else {
params['var-' + variable.name] = variable.getValueForUrl();
} }
params['var-' + variable.name] = value;
}); });
}; };
......
...@@ -8,6 +8,7 @@ export interface Variable { ...@@ -8,6 +8,7 @@ export interface Variable {
updateOptions(); updateOptions();
dependsOn(variable); dependsOn(variable);
setValueFromUrl(urlValue); setValueFromUrl(urlValue);
getValueForUrl();
getModel(); getModel();
} }
......
...@@ -14,6 +14,7 @@ export class VariableSrv { ...@@ -14,6 +14,7 @@ export class VariableSrv {
constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) { constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) {
// update time variant variables // update time variant variables
$rootScope.$on('refresh', this.onDashboardRefresh.bind(this), $rootScope); $rootScope.$on('refresh', this.onDashboardRefresh.bind(this), $rootScope);
$rootScope.$on('template-variable-value-updated', this.updateUrlParamsWithCurrentVariables.bind(this), $rootScope);
} }
init(dashboard) { init(dashboard) {
...@@ -210,6 +211,23 @@ export class VariableSrv { ...@@ -210,6 +211,23 @@ export class VariableSrv {
this.selectOptionsForCurrentValue(variable); this.selectOptionsForCurrentValue(variable);
return this.variableUpdated(variable); return this.variableUpdated(variable);
} }
updateUrlParamsWithCurrentVariables() {
// update url
var params = this.$location.search();
// remove variable params
_.each(params, function(value, key) {
if (key.indexOf('var-') === 0) {
delete params[key];
}
});
// add new values
this.templateSrv.fillVariableValuesForUrl(params);
// update url
this.$location.search(params);
}
} }
coreModule.service('variableSrv', VariableSrv); coreModule.service('variableSrv', VariableSrv);
...@@ -56,9 +56,9 @@ export default class InfluxDatasource { ...@@ -56,9 +56,9 @@ export default class InfluxDatasource {
// apply add hoc filters // apply add hoc filters
for (let variable of this.templateSrv.variables) { for (let variable of this.templateSrv.variables) {
if (variable.type === 'adhoc' && variable.datasource === this.name) { if (variable.type === 'adhoc' && variable.datasource === this.name) {
for (let tag of variable.tags) { for (let filter of variable.filters) {
if (tag.key !== undefined && tag.value !== undefined) { if (filter.key !== undefined && filter.value !== undefined) {
target.tags.push({key: tag.key, value: tag.value, condition: 'AND'}); target.tags.push({key: filter.key, value: filter.value, condition: filter.condition, operator: filter.operator});
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment