mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 09:33:34 -06:00
Merge pull request #15118 from grafana/1909/from-to-template-variables
Time range from & to as template variables
This commit is contained in:
commit
67756c43a7
@ -245,6 +245,11 @@ summarize($myinterval, sum, false)
|
|||||||
|
|
||||||
Grafana has global built-in variables that can be used in expressions in the query editor.
|
Grafana has global built-in variables that can be used in expressions in the query editor.
|
||||||
|
|
||||||
|
### Time range variables
|
||||||
|
|
||||||
|
Grafana has two built in time range variables in `$__from` and `$__to`. They are currently always interpolated
|
||||||
|
as epoch milliseconds.
|
||||||
|
|
||||||
### The $__interval Variable
|
### The $__interval Variable
|
||||||
|
|
||||||
This $__interval variable is similar to the `auto` interval variable that is described above. It can be used as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch) or as a *summarize* function parameter (for Graphite).
|
This $__interval variable is similar to the `auto` interval variable that is described above. It can be used as a parameter to group by time (for InfluxDB, MySQL, Postgres, MSSQL), Date histogram interval (for Elasticsearch) or as a *summarize* function parameter (for Graphite).
|
||||||
|
@ -9,6 +9,7 @@ import sortByKeys from 'app/core/utils/sort_by_keys';
|
|||||||
|
|
||||||
import { PanelModel } from './panel_model';
|
import { PanelModel } from './panel_model';
|
||||||
import { DashboardMigrator } from './dashboard_migration';
|
import { DashboardMigrator } from './dashboard_migration';
|
||||||
|
import { TimeRange } from '@grafana/ui/src';
|
||||||
|
|
||||||
export class DashboardModel {
|
export class DashboardModel {
|
||||||
id: any;
|
id: any;
|
||||||
@ -200,8 +201,8 @@ export class DashboardModel {
|
|||||||
this.events.emit('view-mode-changed', panel);
|
this.events.emit('view-mode-changed', panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
timeRangeUpdated() {
|
timeRangeUpdated(timeRange: TimeRange) {
|
||||||
this.events.emit('time-range-updated');
|
this.events.emit('time-range-updated', timeRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
startRefresh() {
|
startRefresh() {
|
||||||
|
@ -147,7 +147,7 @@ export class TimeSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshDashboard() {
|
refreshDashboard() {
|
||||||
this.dashboard.timeRangeUpdated();
|
this.dashboard.timeRangeUpdated(this.timeRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
private startNextRefreshTimer(afterMs) {
|
private startNextRefreshTimer(afterMs) {
|
||||||
|
@ -135,7 +135,7 @@ export class VariableEditorCtrl {
|
|||||||
$scope.runQuery().then(() => {
|
$scope.runQuery().then(() => {
|
||||||
$scope.reset();
|
$scope.reset();
|
||||||
$scope.mode = 'list';
|
$scope.mode = 'list';
|
||||||
templateSrv.updateTemplateData();
|
templateSrv.updateIndex();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -348,7 +348,7 @@ describe('templateSrv', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateTemplateData with simple value', () => {
|
describe('updateIndex with simple value', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'muuuu' } }]);
|
initTemplateSrv([{ type: 'query', name: 'test', current: { value: 'muuuu' } }]);
|
||||||
});
|
});
|
||||||
@ -476,7 +476,7 @@ describe('templateSrv', () => {
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
_templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
|
_templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
|
||||||
_templateSrv.updateTemplateData();
|
_templateSrv.updateIndex();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace with text except for grafanaVariables', () => {
|
it('should replace with text except for grafanaVariables', () => {
|
||||||
|
@ -8,7 +8,9 @@ describe('VariableSrv', function(this: any) {
|
|||||||
const ctx = {
|
const ctx = {
|
||||||
datasourceSrv: {},
|
datasourceSrv: {},
|
||||||
timeSrv: {
|
timeSrv: {
|
||||||
timeRange: () => {},
|
timeRange: () => {
|
||||||
|
return { from: '2018-01-29', to: '2019-01-29' };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
$rootScope: {
|
$rootScope: {
|
||||||
$on: () => {},
|
$on: () => {},
|
||||||
@ -21,7 +23,7 @@ describe('VariableSrv', function(this: any) {
|
|||||||
init: vars => {
|
init: vars => {
|
||||||
this.variables = vars;
|
this.variables = vars;
|
||||||
},
|
},
|
||||||
updateTemplateData: () => {},
|
updateIndex: () => {},
|
||||||
replace: str =>
|
replace: str =>
|
||||||
str.replace(this.regex, match => {
|
str.replace(this.regex, match => {
|
||||||
return match;
|
return match;
|
||||||
@ -45,7 +47,14 @@ describe('VariableSrv', function(this: any) {
|
|||||||
const ds: any = {};
|
const ds: any = {};
|
||||||
ds.metricFindQuery = () => Promise.resolve(scenario.queryResult);
|
ds.metricFindQuery = () => Promise.resolve(scenario.queryResult);
|
||||||
|
|
||||||
ctx.variableSrv = new VariableSrv(ctx.$rootScope, $q, ctx.$location, ctx.$injector, ctx.templateSrv);
|
ctx.variableSrv = new VariableSrv(
|
||||||
|
ctx.$rootScope,
|
||||||
|
$q,
|
||||||
|
ctx.$location,
|
||||||
|
ctx.$injector,
|
||||||
|
ctx.templateSrv,
|
||||||
|
ctx.timeSrv
|
||||||
|
);
|
||||||
|
|
||||||
ctx.variableSrv.timeSrv = ctx.timeSrv;
|
ctx.variableSrv.timeSrv = ctx.timeSrv;
|
||||||
ctx.datasourceSrv = {
|
ctx.datasourceSrv = {
|
||||||
|
@ -11,13 +11,19 @@ describe('VariableSrv init', function(this: any) {
|
|||||||
this.variables = vars;
|
this.variables = vars;
|
||||||
},
|
},
|
||||||
variableInitialized: () => {},
|
variableInitialized: () => {},
|
||||||
updateTemplateData: () => {},
|
updateIndex: () => {},
|
||||||
replace: str =>
|
replace: str =>
|
||||||
str.replace(this.regex, match => {
|
str.replace(this.regex, match => {
|
||||||
return match;
|
return match;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const timeSrv = {
|
||||||
|
timeRange: () => {
|
||||||
|
return { from: '2018-01-29', to: '2019-01-29' };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const $injector = {} as any;
|
const $injector = {} as any;
|
||||||
const $rootscope = {
|
const $rootscope = {
|
||||||
$on: () => {},
|
$on: () => {},
|
||||||
@ -47,7 +53,8 @@ describe('VariableSrv init', function(this: any) {
|
|||||||
templateSrv,
|
templateSrv,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.variableSrv = new VariableSrv($rootscope, $q, {}, $injector, templateSrv);
|
// @ts-ignore
|
||||||
|
ctx.variableSrv = new VariableSrv($rootscope, $q, {}, $injector, templateSrv, timeSrv);
|
||||||
|
|
||||||
$injector.instantiate = (variable, model) => {
|
$injector.instantiate = (variable, model) => {
|
||||||
return getVarMockConstructor(variable, model, ctx);
|
return getVarMockConstructor(variable, model, ctx);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import kbn from 'app/core/utils/kbn';
|
import kbn from 'app/core/utils/kbn';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { variableRegex } from 'app/features/templating/variable';
|
import { variableRegex } from 'app/features/templating/variable';
|
||||||
|
import { TimeRange } from '@grafana/ui/src';
|
||||||
|
|
||||||
function luceneEscape(value) {
|
function luceneEscape(value) {
|
||||||
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
|
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
|
||||||
@ -13,6 +14,7 @@ export class TemplateSrv {
|
|||||||
private index = {};
|
private index = {};
|
||||||
private grafanaVariables = {};
|
private grafanaVariables = {};
|
||||||
private builtIns = {};
|
private builtIns = {};
|
||||||
|
private timeRange: TimeRange = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.builtIns['__interval'] = { text: '1s', value: '1s' };
|
this.builtIns['__interval'] = { text: '1s', value: '1s' };
|
||||||
@ -20,12 +22,13 @@ export class TemplateSrv {
|
|||||||
this.variables = [];
|
this.variables = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(variables) {
|
init(variables, timeRange?: TimeRange) {
|
||||||
this.variables = variables;
|
this.variables = variables;
|
||||||
this.updateTemplateData();
|
this.timeRange = timeRange;
|
||||||
|
this.updateIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTemplateData() {
|
updateIndex() {
|
||||||
const existsOrEmpty = value => value || value === '';
|
const existsOrEmpty = value => value || value === '';
|
||||||
|
|
||||||
this.index = this.variables.reduce((acc, currentValue) => {
|
this.index = this.variables.reduce((acc, currentValue) => {
|
||||||
@ -34,6 +37,26 @@ export class TemplateSrv {
|
|||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
if (this.timeRange) {
|
||||||
|
const from = this.timeRange.from.valueOf().toString();
|
||||||
|
const to = this.timeRange.to.valueOf().toString();
|
||||||
|
|
||||||
|
this.index = {
|
||||||
|
...this.index,
|
||||||
|
['__from']: {
|
||||||
|
current: { value: from, text: from },
|
||||||
|
},
|
||||||
|
['__to']: {
|
||||||
|
current: { value: to, text: to },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTimeRange(timeRange: TimeRange) {
|
||||||
|
this.timeRange = timeRange;
|
||||||
|
this.updateIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
variableInitialized(variable) {
|
variableInitialized(variable) {
|
||||||
@ -81,8 +104,14 @@ export class TemplateSrv {
|
|||||||
// also the sub-delims "!", "'", "(", ")" and "*" are encoded;
|
// also the sub-delims "!", "'", "(", ")" and "*" are encoded;
|
||||||
// unicode handling uses UTF-8 as in ECMA-262.
|
// unicode handling uses UTF-8 as in ECMA-262.
|
||||||
encodeURIComponentStrict(str) {
|
encodeURIComponentStrict(str) {
|
||||||
return encodeURIComponent(str).replace(/[!'()*]/g, (c) => {
|
return encodeURIComponent(str).replace(/[!'()*]/g, c => {
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase();
|
return (
|
||||||
|
'%' +
|
||||||
|
c
|
||||||
|
.charCodeAt(0)
|
||||||
|
.toString(16)
|
||||||
|
.toUpperCase()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,11 +285,11 @@ export class TemplateSrv {
|
|||||||
|
|
||||||
const value = this.grafanaVariables[variable.current.value];
|
const value = this.grafanaVariables[variable.current.value];
|
||||||
|
|
||||||
return typeof(value) === 'string' ? value : variable.current.text;
|
return typeof value === 'string' ? value : variable.current.text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fillVariableValuesForUrl(params, scopedVars) {
|
fillVariableValuesForUrl(params, scopedVars?) {
|
||||||
_.each(this.variables, variable => {
|
_.each(this.variables, variable => {
|
||||||
if (scopedVars && scopedVars[variable.name] !== void 0) {
|
if (scopedVars && scopedVars[variable.name] !== void 0) {
|
||||||
if (scopedVars[variable.name].skipUrlSync) {
|
if (scopedVars[variable.name].skipUrlSync) {
|
||||||
|
@ -6,23 +6,34 @@ import _ from 'lodash';
|
|||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
import { variableTypes } from './variable';
|
import { variableTypes } from './variable';
|
||||||
import { Graph } from 'app/core/utils/dag';
|
import { Graph } from 'app/core/utils/dag';
|
||||||
|
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||||
|
import { TimeSrv } from 'app/features/dashboard/time_srv';
|
||||||
|
import { DashboardModel } from 'app/features/dashboard/dashboard_model';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { TimeRange } from '@grafana/ui/src';
|
||||||
|
|
||||||
export class VariableSrv {
|
export class VariableSrv {
|
||||||
dashboard: any;
|
dashboard: DashboardModel;
|
||||||
variables: any;
|
variables: any[];
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope, private $q, private $location, private $injector, private templateSrv) {
|
constructor(private $rootScope,
|
||||||
|
private $q,
|
||||||
|
private $location,
|
||||||
|
private $injector,
|
||||||
|
private templateSrv: TemplateSrv,
|
||||||
|
private timeSrv: TimeSrv) {
|
||||||
$rootScope.$on('template-variable-value-updated', this.updateUrlParamsWithCurrentVariables.bind(this), $rootScope);
|
$rootScope.$on('template-variable-value-updated', this.updateUrlParamsWithCurrentVariables.bind(this), $rootScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(dashboard) {
|
init(dashboard: DashboardModel) {
|
||||||
this.dashboard = dashboard;
|
this.dashboard = dashboard;
|
||||||
this.dashboard.events.on('time-range-updated', this.onTimeRangeUpdated.bind(this));
|
this.dashboard.events.on('time-range-updated', this.onTimeRangeUpdated.bind(this));
|
||||||
|
|
||||||
// create working class models representing variables
|
// create working class models representing variables
|
||||||
this.variables = dashboard.templating.list = dashboard.templating.list.map(this.createVariableFromModel.bind(this));
|
this.variables = dashboard.templating.list = dashboard.templating.list.map(this.createVariableFromModel.bind(this));
|
||||||
this.templateSrv.init(this.variables);
|
this.templateSrv.init(this.variables, this.timeSrv.timeRange());
|
||||||
|
|
||||||
// init variables
|
// init variables
|
||||||
for (const variable of this.variables) {
|
for (const variable of this.variables) {
|
||||||
@ -37,11 +48,12 @@ export class VariableSrv {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.templateSrv.updateTemplateData();
|
this.templateSrv.updateIndex();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onTimeRangeUpdated() {
|
onTimeRangeUpdated(timeRange: TimeRange) {
|
||||||
|
this.templateSrv.updateTimeRange(timeRange);
|
||||||
const promises = this.variables.filter(variable => variable.refresh === 2).map(variable => {
|
const promises = this.variables.filter(variable => variable.refresh === 2).map(variable => {
|
||||||
const previousOptions = variable.options.slice();
|
const previousOptions = variable.options.slice();
|
||||||
|
|
||||||
@ -100,14 +112,14 @@ export class VariableSrv {
|
|||||||
|
|
||||||
addVariable(variable) {
|
addVariable(variable) {
|
||||||
this.variables.push(variable);
|
this.variables.push(variable);
|
||||||
this.templateSrv.updateTemplateData();
|
this.templateSrv.updateIndex();
|
||||||
this.dashboard.updateSubmenuVisibility();
|
this.dashboard.updateSubmenuVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeVariable(variable) {
|
removeVariable(variable) {
|
||||||
const index = _.indexOf(this.variables, variable);
|
const index = _.indexOf(this.variables, variable);
|
||||||
this.variables.splice(index, 1);
|
this.variables.splice(index, 1);
|
||||||
this.templateSrv.updateTemplateData();
|
this.templateSrv.updateIndex();
|
||||||
this.dashboard.updateSubmenuVisibility();
|
this.dashboard.updateSubmenuVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ export function TemplateSrvStub(this: any) {
|
|||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
this.fillVariableValuesForUrl = () => {};
|
this.fillVariableValuesForUrl = () => {};
|
||||||
this.updateTemplateData = () => {};
|
this.updateIndex = () => {};
|
||||||
this.variableExists = () => {
|
this.variableExists = () => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user