mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard: Use Interval variable in DashboardScene (#75836)
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
parent
157ea31b03
commit
8dfd918200
@ -62,7 +62,7 @@ exports[`transformSceneToSaveModel Annotations should transform annotations to s
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`transformSceneToSaveModel Given a scene with rows Should transform back to peristed model 1`] = `
|
||||
exports[`transformSceneToSaveModel Given a scene with rows Should transform back to persisted model 1`] = `
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
@ -242,7 +242,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`transformSceneToSaveModel Given a simple scene Should transform back to peristed model 1`] = `
|
||||
exports[`transformSceneToSaveModel Given a simple scene with variables Should transform back to persisted model 1`] = `
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
@ -448,6 +448,76 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"auto": true,
|
||||
"auto_count": 30,
|
||||
"auto_min": "10s",
|
||||
"current": {
|
||||
"text": "1m",
|
||||
"value": "1m",
|
||||
},
|
||||
"hide": 2,
|
||||
"name": "intervalVar",
|
||||
"query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
|
||||
"refresh": 2,
|
||||
"type": "interval",
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": [
|
||||
"a",
|
||||
],
|
||||
"value": [
|
||||
"a",
|
||||
],
|
||||
},
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"name": "customVar",
|
||||
"options": [],
|
||||
"query": "a, b, c",
|
||||
"type": "custom",
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "gdev-testdata",
|
||||
"value": "PD8C576611E62080A",
|
||||
},
|
||||
"includeAll": false,
|
||||
"name": "dsVar",
|
||||
"options": [],
|
||||
"query": "grafana-testdata-datasource",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "datasource",
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "A",
|
||||
"value": "A",
|
||||
},
|
||||
"includeAll": false,
|
||||
"name": "query0",
|
||||
"options": [],
|
||||
"query": {
|
||||
"query": "*",
|
||||
"refId": "StandardVariableQuery",
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"type": "query",
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"text": "test",
|
||||
"value": "test",
|
||||
},
|
||||
"hide": 2,
|
||||
"name": "constant",
|
||||
"query": "test",
|
||||
"skipUrlSync": true,
|
||||
"type": "constant",
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
|
@ -1,6 +1,15 @@
|
||||
import { SceneVariableSet, QueryVariable, CustomVariable, DataSourceVariable, ConstantVariable } from '@grafana/scenes';
|
||||
import {
|
||||
SceneVariableSet,
|
||||
QueryVariable,
|
||||
CustomVariable,
|
||||
DataSourceVariable,
|
||||
ConstantVariable,
|
||||
IntervalVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { VariableModel, VariableHide, VariableRefresh, VariableSort } from '@grafana/schema';
|
||||
|
||||
import { getIntervalsQueryFromNewIntervalModel } from '../utils/utils';
|
||||
|
||||
export function sceneVariablesSetToVariables(set: SceneVariableSet) {
|
||||
const variables: VariableModel[] = [];
|
||||
for (const variable of set.state.variables) {
|
||||
@ -78,6 +87,22 @@ export function sceneVariablesSetToVariables(set: SceneVariableSet) {
|
||||
query: variable.state.value,
|
||||
hide: VariableHide.hideVariable,
|
||||
});
|
||||
} else if (variable instanceof IntervalVariable) {
|
||||
const intervals = getIntervalsQueryFromNewIntervalModel(variable.state.intervals);
|
||||
variables.push({
|
||||
...commonProperties,
|
||||
current: {
|
||||
text: variable.state.value,
|
||||
value: variable.state.value,
|
||||
},
|
||||
query: intervals,
|
||||
hide: VariableHide.hideVariable,
|
||||
refresh: variable.state.refresh,
|
||||
// @ts-expect-error ?? how to fix this without adding the ts-expect-error
|
||||
auto: variable.state.autoEnabled,
|
||||
auto_min: variable.state.autoMinInterval,
|
||||
auto_count: variable.state.autoStepCount,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Unsupported variable type');
|
||||
}
|
||||
|
@ -202,6 +202,105 @@
|
||||
"name": "Filters",
|
||||
"skipUrlSync": false,
|
||||
"type": "adhoc"
|
||||
},
|
||||
{
|
||||
"auto": true,
|
||||
"auto_count": 30,
|
||||
"auto_min": "10s",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "1m",
|
||||
"value": "1m"
|
||||
},
|
||||
"hide": 0,
|
||||
"name": "intervalVar",
|
||||
"query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d",
|
||||
"queryValue": "",
|
||||
"refresh": 2,
|
||||
"skipUrlSync": false,
|
||||
"type": "interval"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"selected": true,
|
||||
"text": ["a"],
|
||||
"value": ["a"]
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"multi": true,
|
||||
"name": "customVar",
|
||||
"options": [
|
||||
{
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
"value": "$__all"
|
||||
},
|
||||
{
|
||||
"selected": true,
|
||||
"text": "a",
|
||||
"value": "a"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "b",
|
||||
"value": "b"
|
||||
},
|
||||
{
|
||||
"selected": false,
|
||||
"text": "c",
|
||||
"value": "c"
|
||||
}
|
||||
],
|
||||
"query": "a, b, c",
|
||||
"skipUrlSync": false,
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "gdev-testdata",
|
||||
"value": "PD8C576611E62080A"
|
||||
},
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"name": "dsVar",
|
||||
"options": [],
|
||||
"query": "grafana-testdata-datasource",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "A",
|
||||
"value": "A"
|
||||
},
|
||||
"definition": "*",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"name": "query0",
|
||||
"options": [],
|
||||
"query": {
|
||||
"query": "*",
|
||||
"refId": "StandardVariableQuery"
|
||||
},
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"type": "query"
|
||||
},
|
||||
{
|
||||
"hide": 2,
|
||||
"name": "constant",
|
||||
"query": "test",
|
||||
"skipUrlSync": false,
|
||||
"type": "constant"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -637,7 +637,56 @@ describe('transformSaveModelToScene', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['interval', 'textbox', 'system'])('should throw for unsupported (yet) variables', (type) => {
|
||||
it('should migrate interval variable', () => {
|
||||
const variable = {
|
||||
name: 'intervalVar',
|
||||
label: 'Interval Label',
|
||||
type: 'interval' as VariableType,
|
||||
rootStateKey: 'N4XLmH5Vz',
|
||||
auto: false,
|
||||
refresh: 2,
|
||||
auto_count: 30,
|
||||
auto_min: '10s',
|
||||
current: {
|
||||
selected: true,
|
||||
text: '1m',
|
||||
value: '1m',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
selected: true,
|
||||
text: '1m',
|
||||
value: '1m',
|
||||
},
|
||||
],
|
||||
query: '1m, 5m, 15m, 30m, 1h, 6h, 12h, 1d, 7d, 14d, 30d',
|
||||
id: 'intervalVar',
|
||||
global: false,
|
||||
index: 4,
|
||||
hide: 0,
|
||||
skipUrlSync: false,
|
||||
state: 'Done',
|
||||
error: null,
|
||||
description: null,
|
||||
};
|
||||
const migrated = createSceneVariableFromVariableModel(variable);
|
||||
const { key, ...rest } = migrated.state;
|
||||
expect(rest).toEqual({
|
||||
label: 'Interval Label',
|
||||
autoEnabled: false,
|
||||
autoMinInterval: '10s',
|
||||
autoStepCount: 30,
|
||||
description: null,
|
||||
refresh: 2,
|
||||
intervals: ['1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d', '7d', '14d', '30d'],
|
||||
hide: 0,
|
||||
name: 'intervalVar',
|
||||
skipUrlSync: false,
|
||||
type: 'interval',
|
||||
value: '1m',
|
||||
});
|
||||
});
|
||||
it.each(['textbox', 'system'])('should throw for unsupported (yet) variables', (type) => {
|
||||
const variable = {
|
||||
name: 'query0',
|
||||
type: type as VariableType,
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
ConstantVariableModel,
|
||||
CustomVariableModel,
|
||||
DataSourceVariableModel,
|
||||
IntervalVariableModel,
|
||||
QueryVariableModel,
|
||||
VariableModel,
|
||||
} from '@grafana/data';
|
||||
@ -19,6 +20,7 @@ import {
|
||||
DataSourceVariable,
|
||||
QueryVariable,
|
||||
ConstantVariable,
|
||||
IntervalVariable,
|
||||
SceneRefreshPicker,
|
||||
SceneGridItem,
|
||||
SceneObject,
|
||||
@ -44,7 +46,11 @@ import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||
import { setDashboardPanelContext } from '../scene/setDashboardPanelContext';
|
||||
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
||||
import { getVizPanelKeyForPanelId } from '../utils/utils';
|
||||
import {
|
||||
getCurrentValueForOldIntervalModel,
|
||||
getIntervalsFromOldIntervalModel,
|
||||
getVizPanelKeyForPanelId,
|
||||
} from '../utils/utils';
|
||||
|
||||
import { getAngularPanelMigrationHandler } from './angularMigration';
|
||||
|
||||
@ -292,6 +298,21 @@ export function createSceneVariableFromVariableModel(variable: VariableModel): S
|
||||
isMulti: variable.multi,
|
||||
hide: variable.hide,
|
||||
});
|
||||
} else if (isIntervalVariable(variable)) {
|
||||
const intervals = getIntervalsFromOldIntervalModel(variable);
|
||||
const currentInterval = getCurrentValueForOldIntervalModel(variable, intervals);
|
||||
return new IntervalVariable({
|
||||
...commonProperties,
|
||||
value: currentInterval,
|
||||
description: variable.description,
|
||||
intervals: intervals,
|
||||
autoEnabled: variable.auto,
|
||||
autoStepCount: variable.auto_count,
|
||||
autoMinInterval: variable.auto_min,
|
||||
refresh: variable.refresh,
|
||||
skipUrlSync: variable.skipUrlSync,
|
||||
hide: variable.hide,
|
||||
});
|
||||
} else if (isConstantVariable(variable)) {
|
||||
return new ConstantVariable({
|
||||
...commonProperties,
|
||||
@ -323,6 +344,7 @@ export function buildGridItemForLibPanel(panel: PanelModel) {
|
||||
height: panel.gridPos.h,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
|
||||
const vizPanelState: VizPanelState = {
|
||||
key: getVizPanelKeyForPanelId(panel.id),
|
||||
@ -382,4 +404,5 @@ const isCustomVariable = (v: VariableModel): v is CustomVariableModel => v.type
|
||||
const isQueryVariable = (v: VariableModel): v is QueryVariableModel => v.type === 'query';
|
||||
const isDataSourceVariable = (v: VariableModel): v is DataSourceVariableModel => v.type === 'datasource';
|
||||
const isConstantVariable = (v: VariableModel): v is ConstantVariableModel => v.type === 'constant';
|
||||
const isIntervalVariable = (v: VariableModel): v is IntervalVariableModel => v.type === 'interval';
|
||||
const isAdhocVariable = (v: VariableModel): v is AdHocVariableModel => v.type === 'adhoc';
|
||||
|
@ -138,8 +138,8 @@ jest.mock('@grafana/runtime', () => ({
|
||||
},
|
||||
}));
|
||||
describe('transformSceneToSaveModel', () => {
|
||||
describe('Given a simple scene', () => {
|
||||
it('Should transform back to peristed model', () => {
|
||||
describe('Given a simple scene with variables', () => {
|
||||
it('Should transform back to persisted model', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
|
||||
const saveModel = transformSceneToSaveModel(scene);
|
||||
|
||||
@ -148,7 +148,7 @@ describe('transformSceneToSaveModel', () => {
|
||||
});
|
||||
|
||||
describe('Given a scene with rows', () => {
|
||||
it('Should transform back to peristed model', () => {
|
||||
it('Should transform back to persisted model', () => {
|
||||
const scene = transformSaveModelToScene({ dashboard: repeatingRowsAndPanelsDashboardJson as any, meta: {} });
|
||||
const saveModel = transformSceneToSaveModel(scene);
|
||||
const row2: RowPanel = saveModel.panels![2] as RowPanel;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UrlQueryMap, urlUtil } from '@grafana/data';
|
||||
import { IntervalVariableModel, UrlQueryMap, urlUtil } from '@grafana/data';
|
||||
import { config, locationSearchToObject } from '@grafana/runtime';
|
||||
import {
|
||||
MultiValueVariable,
|
||||
@ -8,6 +8,7 @@ import {
|
||||
SceneQueryRunner,
|
||||
VizPanel,
|
||||
} from '@grafana/scenes';
|
||||
import { initialIntervalVariableModelState } from 'app/features/variables/interval/reducer';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
|
||||
@ -150,6 +151,58 @@ export function getMultiVariableValues(variable: MultiValueVariable) {
|
||||
};
|
||||
}
|
||||
|
||||
// Transform old interval model to new interval model from scenes
|
||||
export function getIntervalsFromOldIntervalModel(variable: IntervalVariableModel): string[] {
|
||||
// separate intervals by quotes either single or double
|
||||
const matchIntervals = variable.query.match(/(["'])(.*?)\1|\w+/g);
|
||||
|
||||
// If no intervals are found in query, return the initial state of the interval reducer.
|
||||
if (!matchIntervals) {
|
||||
return initialIntervalVariableModelState.query?.split(',') ?? [];
|
||||
}
|
||||
const uniqueIntervals = new Set<string>();
|
||||
|
||||
// when options are defined in variable.query
|
||||
const intervals = matchIntervals.reduce((uniqueIntervals: Set<string>, text: string) => {
|
||||
// Remove surrounding quotes from the interval value.
|
||||
const intervalValue = text.replace(/["']+/g, '');
|
||||
|
||||
// Skip intervals that start with "$__auto_interval_",scenes will handle them.
|
||||
if (intervalValue.startsWith('$__auto_interval_')) {
|
||||
return uniqueIntervals;
|
||||
}
|
||||
|
||||
// Add the interval if it's not already in the Set.
|
||||
uniqueIntervals.add(intervalValue);
|
||||
return uniqueIntervals;
|
||||
}, uniqueIntervals);
|
||||
|
||||
return Array.from(intervals);
|
||||
}
|
||||
|
||||
// Transform new interval scene model to old interval core model
|
||||
export function getIntervalsQueryFromNewIntervalModel(intervals: string[]): string {
|
||||
const variableQuery = Array.isArray(intervals) ? intervals.join(',') : '';
|
||||
return variableQuery;
|
||||
}
|
||||
|
||||
export function getCurrentValueForOldIntervalModel(variable: IntervalVariableModel, intervals: string[]): string {
|
||||
const selectedInterval = Array.isArray(variable.current.value) ? variable.current.value[0] : variable.current.value;
|
||||
|
||||
// If the interval is the old auto format, return the new auto interval from scenes.
|
||||
if (selectedInterval.startsWith('$__auto_interval_')) {
|
||||
return '$__auto';
|
||||
}
|
||||
|
||||
// Check if the selected interval is valid.
|
||||
if (intervals.includes(selectedInterval)) {
|
||||
return selectedInterval;
|
||||
}
|
||||
|
||||
// If the selected interval is not valid, return the first valid interval.
|
||||
return intervals[0];
|
||||
}
|
||||
|
||||
export function getQueryRunnerFor(sceneObject: SceneObject | undefined): SceneQueryRunner | undefined {
|
||||
if (!sceneObject) {
|
||||
return undefined;
|
||||
|
Loading…
Reference in New Issue
Block a user