mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboards Schema V2: ResponseTransformers: Transform remaining variables when ensuring v2 (#98777)
* add missing vars * Don't create undefined variable fields * tests * Fix test; remove allValue from groupBy * Fix tests * betterer * Use @ts-expect-error * betterer
This commit is contained in:
parent
40baca699a
commit
4f337b99d4
@ -753,8 +753,6 @@ GroupByVariableSpec: {
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
|
@ -380,7 +380,6 @@ export const handyTestingSchema: DashboardV2Spec = {
|
||||
},
|
||||
description: 'A group by variable',
|
||||
hide: 'dontHide',
|
||||
includeAll: false,
|
||||
label: 'Group By Variable',
|
||||
multi: false,
|
||||
name: 'groupByVar',
|
||||
|
@ -1109,8 +1109,6 @@ export interface GroupByVariableSpec {
|
||||
current: VariableOption;
|
||||
options: VariableOption[];
|
||||
multi: boolean;
|
||||
includeAll: boolean;
|
||||
allValue?: string;
|
||||
label?: string;
|
||||
hide: VariableHide;
|
||||
skipUrlSync: boolean;
|
||||
@ -1122,7 +1120,6 @@ export const defaultGroupByVariableSpec = (): GroupByVariableSpec => ({
|
||||
current: { text: "", value: "", },
|
||||
options: [],
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
hide: "dontHide",
|
||||
skipUrlSync: false,
|
||||
});
|
||||
|
@ -488,7 +488,6 @@ describe('DashboardSceneSerializer', () => {
|
||||
},
|
||||
],
|
||||
multi: false,
|
||||
includeAll: false,
|
||||
hide: 'dontHide',
|
||||
skipUrlSync: false,
|
||||
},
|
||||
|
@ -305,7 +305,6 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
|
||||
},
|
||||
"description": "A group by variable",
|
||||
"hide": "dontHide",
|
||||
"includeAll": false,
|
||||
"label": "Group By Variable",
|
||||
"multi": false,
|
||||
"name": "groupByVar",
|
||||
|
@ -1230,7 +1230,6 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
"description": "test-desc",
|
||||
"hide": "dontHide",
|
||||
"includeAll": false,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
|
@ -407,7 +407,6 @@ export function sceneVariablesSetToSchemaV2Variables(
|
||||
})) || [],
|
||||
current: currentVariableOption,
|
||||
multi: variable.state.isMulti || false,
|
||||
includeAll: variable.state.includeAll || false,
|
||||
},
|
||||
};
|
||||
variables.push(groupVariable);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DataQuery } from '@grafana/schema';
|
||||
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
||||
import { DataQuery, VariableModel, VariableRefresh } from '@grafana/schema';
|
||||
import { DashboardV2Spec, VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
||||
import {
|
||||
AnnoKeyCreatedBy,
|
||||
AnnoKeyDashboardGnetId,
|
||||
@ -10,6 +10,10 @@ import {
|
||||
AnnoKeyUpdatedTimestamp,
|
||||
} from 'app/features/apiserver/types';
|
||||
import { getDefaultDataSourceRef } from 'app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2';
|
||||
import {
|
||||
transformVariableHideToEnum,
|
||||
transformVariableRefreshToEnum,
|
||||
} from 'app/features/dashboard-scene/serialization/transformToV2TypesUtils';
|
||||
import { DashboardDataDTO, DashboardDTO } from 'app/types';
|
||||
|
||||
import { getDefaultDatasource, getPanelQueries, ResponseTransformers } from './ResponseTransformers';
|
||||
@ -119,6 +123,164 @@ describe('ResponseTransformers', () => {
|
||||
annotations: {
|
||||
list: [],
|
||||
},
|
||||
templating: {
|
||||
list: [
|
||||
{
|
||||
type: 'query',
|
||||
name: 'var1',
|
||||
label: 'query var',
|
||||
description: 'query var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
multi: true,
|
||||
includeAll: true,
|
||||
current: { value: '1', text: '1' },
|
||||
options: [
|
||||
{ selected: true, text: '1', value: '1' },
|
||||
{ selected: false, text: '2', value: '2' },
|
||||
],
|
||||
refresh: VariableRefresh.onTimeRangeChanged,
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'abc',
|
||||
},
|
||||
regex: '.*',
|
||||
sort: 1,
|
||||
query: {
|
||||
expr: 'sum(query)',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'datasource',
|
||||
name: 'var2',
|
||||
label: 'datasource var',
|
||||
description: 'datasource var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
multi: true,
|
||||
includeAll: true,
|
||||
current: { value: 'PromTest', text: 'PromTest' },
|
||||
options: [
|
||||
{ selected: true, text: 'PromTest', value: 'PromTest' },
|
||||
{ selected: false, text: 'Grafana', value: 'Grafana' },
|
||||
],
|
||||
refresh: VariableRefresh.onTimeRangeChanged,
|
||||
regex: '.*',
|
||||
sort: 1,
|
||||
query: 'sum(query)',
|
||||
},
|
||||
{
|
||||
type: 'custom',
|
||||
name: 'var3',
|
||||
label: 'custom var',
|
||||
description: 'custom var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
multi: true,
|
||||
includeAll: true,
|
||||
current: { value: '1', text: '1' },
|
||||
query: '1,2,3',
|
||||
options: [
|
||||
{ selected: true, text: '1', value: '1' },
|
||||
{ selected: false, text: '2', value: '2' },
|
||||
],
|
||||
allValue: '1,2,3',
|
||||
},
|
||||
{
|
||||
type: 'adhoc',
|
||||
name: 'var4',
|
||||
label: 'adhoc var',
|
||||
description: 'adhoc var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'abc',
|
||||
},
|
||||
// @ts-expect-error
|
||||
baseFilters: [{ key: 'key1', operator: 'AND' }],
|
||||
filters: [],
|
||||
defaultKeys: [],
|
||||
},
|
||||
{
|
||||
type: 'constant',
|
||||
name: 'var5',
|
||||
label: 'constant var',
|
||||
description: 'constant var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
current: { value: '1', text: '0' },
|
||||
query: '1',
|
||||
},
|
||||
{
|
||||
type: 'interval',
|
||||
name: 'var6',
|
||||
label: 'interval var',
|
||||
description: 'interval var description',
|
||||
skipUrlSync: false,
|
||||
query: '1m,10m,30m,1h',
|
||||
hide: 0,
|
||||
current: {
|
||||
value: 'auto',
|
||||
text: 'auto',
|
||||
},
|
||||
refresh: VariableRefresh.onTimeRangeChanged,
|
||||
options: [
|
||||
{
|
||||
selected: true,
|
||||
text: '1m',
|
||||
value: '1m',
|
||||
},
|
||||
{
|
||||
selected: false,
|
||||
text: '10m',
|
||||
value: '10m',
|
||||
},
|
||||
{
|
||||
selected: false,
|
||||
text: '30m',
|
||||
value: '30m',
|
||||
},
|
||||
{
|
||||
selected: false,
|
||||
text: '1h',
|
||||
value: '1h',
|
||||
},
|
||||
],
|
||||
// @ts-expect-error
|
||||
auto: false,
|
||||
auto_min: '1s',
|
||||
auto_count: 1,
|
||||
},
|
||||
{
|
||||
type: 'textbox',
|
||||
name: 'var7',
|
||||
label: 'textbox var',
|
||||
description: 'textbox var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
current: { value: '1', text: '1' },
|
||||
query: '1',
|
||||
},
|
||||
{
|
||||
type: 'groupby',
|
||||
name: 'var8',
|
||||
label: 'groupby var',
|
||||
description: 'groupby var description',
|
||||
skipUrlSync: false,
|
||||
hide: 0,
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'abc',
|
||||
},
|
||||
options: [
|
||||
{ selected: true, text: '1', value: '1' },
|
||||
{ selected: false, text: '2', value: '2' },
|
||||
],
|
||||
current: { value: ['1'], text: ['1'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const dto: DashboardWithAccessInfo<DashboardDataDTO> = {
|
||||
@ -142,7 +304,6 @@ describe('ResponseTransformers', () => {
|
||||
metadata: {
|
||||
name: 'dashboard-uid',
|
||||
resourceVersion: '1',
|
||||
|
||||
creationTimestamp: '2023-01-01T00:00:00Z',
|
||||
annotations: {
|
||||
[AnnoKeyCreatedBy]: 'user1',
|
||||
@ -188,6 +349,14 @@ describe('ResponseTransformers', () => {
|
||||
expect(spec.timeSettings.weekStart).toBe(dashboardV1.weekStart);
|
||||
expect(spec.links).toEqual(dashboardV1.links);
|
||||
expect(spec.annotations).toEqual([]);
|
||||
validateVariablesV1ToV2(spec.variables[0], dashboardV1.templating?.list?.[0]);
|
||||
validateVariablesV1ToV2(spec.variables[1], dashboardV1.templating?.list?.[1]);
|
||||
validateVariablesV1ToV2(spec.variables[2], dashboardV1.templating?.list?.[2]);
|
||||
validateVariablesV1ToV2(spec.variables[3], dashboardV1.templating?.list?.[3]);
|
||||
validateVariablesV1ToV2(spec.variables[4], dashboardV1.templating?.list?.[4]);
|
||||
validateVariablesV1ToV2(spec.variables[5], dashboardV1.templating?.list?.[5]);
|
||||
validateVariablesV1ToV2(spec.variables[6], dashboardV1.templating?.list?.[6]);
|
||||
validateVariablesV1ToV2(spec.variables[7], dashboardV1.templating?.list?.[7]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -399,3 +568,79 @@ describe('ResponseTransformers', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function validateVariablesV1ToV2(v2: VariableKind, v1: VariableModel | undefined) {
|
||||
if (!v1) {
|
||||
return expect(v1).toBeDefined();
|
||||
}
|
||||
|
||||
const v1Common = {
|
||||
name: v1.name,
|
||||
label: v1.label,
|
||||
description: v1.description,
|
||||
hide: transformVariableHideToEnum(v1.hide),
|
||||
skipUrlSync: v1.skipUrlSync,
|
||||
};
|
||||
const v2Common = {
|
||||
name: v2.spec.name,
|
||||
label: v2.spec.label,
|
||||
description: v2.spec.description,
|
||||
hide: v2.spec.hide,
|
||||
skipUrlSync: v2.spec.skipUrlSync,
|
||||
};
|
||||
|
||||
expect(v2Common).toEqual(v1Common);
|
||||
|
||||
if (v2.kind === 'QueryVariable') {
|
||||
expect(v2.spec.datasource).toEqual(v1.datasource);
|
||||
expect(v2.spec.query).toEqual({
|
||||
kind: v1.datasource?.type,
|
||||
spec: {
|
||||
...(typeof v1.query === 'object' ? v1.query : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (v2.kind === 'DatasourceVariable') {
|
||||
expect(v2.spec.pluginId).toBe(v1.query);
|
||||
expect(v2.spec.refresh).toBe(transformVariableRefreshToEnum(v1.refresh));
|
||||
}
|
||||
|
||||
if (v2.kind === 'CustomVariable') {
|
||||
expect(v2.spec.query).toBe(v1.query);
|
||||
expect(v2.spec.options).toEqual(v1.options);
|
||||
}
|
||||
|
||||
if (v2.kind === 'AdhocVariable') {
|
||||
expect(v2.spec.datasource).toEqual(v1.datasource);
|
||||
expect(v2.spec.filters).toEqual([]);
|
||||
// @ts-expect-error
|
||||
expect(v2.spec.baseFilters).toEqual(v1.baseFilters);
|
||||
}
|
||||
|
||||
if (v2.kind === 'ConstantVariable') {
|
||||
expect(v2.spec.query).toBe(v1.query);
|
||||
}
|
||||
|
||||
if (v2.kind === 'IntervalVariable') {
|
||||
expect(v2.spec.query).toBe(v1.query);
|
||||
expect(v2.spec.options).toEqual(v1.options);
|
||||
expect(v2.spec.current).toEqual(v1.current);
|
||||
// @ts-expect-error
|
||||
expect(v2.spec.auto).toBe(v1.auto);
|
||||
// @ts-expect-error
|
||||
expect(v2.spec.auto_min).toBe(v1.auto_min);
|
||||
// @ts-expect-error
|
||||
expect(v2.spec.auto_count).toBe(v1.auto_count);
|
||||
}
|
||||
|
||||
if (v2.kind === 'TextVariable') {
|
||||
expect(v2.spec.query).toBe(v1.query);
|
||||
expect(v2.spec.current).toEqual(v1.current);
|
||||
}
|
||||
|
||||
if (v2.kind === 'GroupByVariable') {
|
||||
expect(v2.spec.datasource).toEqual(v1.datasource);
|
||||
expect(v2.spec.options).toEqual(v1.options);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { TypedVariableModel } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { AnnotationQuery, DataQuery, DataSourceRef, Panel, VariableModel } from '@grafana/schema';
|
||||
import { AnnotationQuery, DataQuery, DataSourceRef, Panel } from '@grafana/schema';
|
||||
import {
|
||||
AnnotationQueryKind,
|
||||
DashboardV2Spec,
|
||||
@ -11,6 +12,12 @@ import {
|
||||
PanelQueryKind,
|
||||
QueryVariableKind,
|
||||
TransformationKind,
|
||||
AdhocVariableKind,
|
||||
CustomVariableKind,
|
||||
ConstantVariableKind,
|
||||
IntervalVariableKind,
|
||||
TextVariableKind,
|
||||
GroupByVariableKind,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
|
||||
import { DataTransformerConfig } from '@grafana/schema/src/raw/dashboard/x/dashboard_types.gen';
|
||||
import {
|
||||
@ -55,6 +62,8 @@ export function ensureV2Response(
|
||||
const timeSettingsDefaults = defaultTimeSettingsSpec();
|
||||
const dashboardDefaults = defaultDashboardV2Spec();
|
||||
const [elements, layout] = getElementsFromPanels(dashboard.panels || []);
|
||||
// @ts-expect-error - dashboard.templating.list is VariableModel[] and we need TypedVariableModel[] here
|
||||
// that would allow accessing unique properties for each variable type that the API returns
|
||||
const variables = getVariables(dashboard.templating?.list || []);
|
||||
const annotations = getAnnotations(dashboard.annotations?.list || []);
|
||||
|
||||
@ -368,9 +377,17 @@ function getPanelTransformations(transformations: DataTransformerConfig[]): Tran
|
||||
});
|
||||
}
|
||||
|
||||
function getVariables(vars: VariableModel[]): DashboardV2Spec['variables'] {
|
||||
function getVariables(vars: TypedVariableModel[]): DashboardV2Spec['variables'] {
|
||||
const variables: DashboardV2Spec['variables'] = [];
|
||||
for (const v of vars) {
|
||||
const commonProperties = {
|
||||
name: v.name,
|
||||
label: v.label,
|
||||
...(v.description && { description: v.description }),
|
||||
skipUrlSync: Boolean(v.skipUrlSync),
|
||||
hide: transformVariableHideToEnum(v.hide),
|
||||
};
|
||||
|
||||
switch (v.type) {
|
||||
case 'query':
|
||||
let query = v.query || {};
|
||||
@ -383,17 +400,17 @@ function getVariables(vars: VariableModel[]): DashboardV2Spec['variables'] {
|
||||
const qv: QueryVariableKind = {
|
||||
kind: 'QueryVariable',
|
||||
spec: {
|
||||
name: v.name,
|
||||
label: v.label,
|
||||
hide: transformVariableHideToEnum(v.hide),
|
||||
skipUrlSync: Boolean(v.skipUrlSync),
|
||||
...commonProperties,
|
||||
multi: Boolean(v.multi),
|
||||
includeAll: Boolean(v.includeAll),
|
||||
allValue: v.allValue,
|
||||
current: v.current || { text: '', value: '' },
|
||||
...(v.allValue && { allValue: v.allValue }),
|
||||
current: {
|
||||
value: v.current.value,
|
||||
text: v.current.text,
|
||||
},
|
||||
options: v.options || [],
|
||||
refresh: transformVariableRefreshToEnum(v.refresh),
|
||||
datasource: v.datasource ?? undefined,
|
||||
...(v.datasource && { datasource: v.datasource }),
|
||||
regex: v.regex || '',
|
||||
sort: transformSortVariableToEnum(v.sort),
|
||||
query: {
|
||||
@ -416,23 +433,119 @@ function getVariables(vars: VariableModel[]): DashboardV2Spec['variables'] {
|
||||
const dv: DatasourceVariableKind = {
|
||||
kind: 'DatasourceVariable',
|
||||
spec: {
|
||||
name: v.name,
|
||||
label: v.label,
|
||||
hide: transformVariableHideToEnum(v.hide),
|
||||
skipUrlSync: Boolean(v.skipUrlSync),
|
||||
...commonProperties,
|
||||
multi: Boolean(v.multi),
|
||||
includeAll: Boolean(v.includeAll),
|
||||
allValue: v.allValue,
|
||||
current: v.current || { text: '', value: '' },
|
||||
...(v.allValue && { allValue: v.allValue }),
|
||||
current: {
|
||||
value: v.current.value,
|
||||
text: v.current.text,
|
||||
},
|
||||
options: v.options || [],
|
||||
refresh: transformVariableRefreshToEnum(v.refresh),
|
||||
pluginId,
|
||||
regex: v.regex || '',
|
||||
description: v.description || '',
|
||||
},
|
||||
};
|
||||
variables.push(dv);
|
||||
break;
|
||||
case 'custom':
|
||||
const cv: CustomVariableKind = {
|
||||
kind: 'CustomVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
query: v.query,
|
||||
current: {
|
||||
value: v.current.value,
|
||||
text: v.current.text,
|
||||
},
|
||||
options: v.options,
|
||||
multi: v.multi,
|
||||
includeAll: v.includeAll,
|
||||
...(v.allValue && { allValue: v.allValue }),
|
||||
},
|
||||
};
|
||||
variables.push(cv);
|
||||
break;
|
||||
case 'adhoc':
|
||||
const av: AdhocVariableKind = {
|
||||
kind: 'AdhocVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
datasource: v.datasource || getDefaultDatasource(),
|
||||
baseFilters: v.baseFilters || [],
|
||||
filters: v.filters || [],
|
||||
defaultKeys: v.defaultKeys || [],
|
||||
},
|
||||
};
|
||||
variables.push(av);
|
||||
break;
|
||||
case 'constant':
|
||||
const cnts: ConstantVariableKind = {
|
||||
kind: 'ConstantVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
current: {
|
||||
value: v.current.value,
|
||||
// Constant variable doesn't use text state
|
||||
text: v.current.value,
|
||||
},
|
||||
query: v.query,
|
||||
},
|
||||
};
|
||||
variables.push(cnts);
|
||||
break;
|
||||
case 'interval':
|
||||
const intrv: IntervalVariableKind = {
|
||||
kind: 'IntervalVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
current: {
|
||||
value: v.current.value,
|
||||
// Interval variable doesn't use text state
|
||||
text: v.current.value,
|
||||
},
|
||||
query: v.query,
|
||||
refresh: 'onTimeRangeChanged',
|
||||
options: v.options,
|
||||
auto: v.auto,
|
||||
auto_min: v.auto_min,
|
||||
auto_count: v.auto_count,
|
||||
},
|
||||
};
|
||||
variables.push(intrv);
|
||||
break;
|
||||
case 'textbox':
|
||||
const tx: TextVariableKind = {
|
||||
kind: 'TextVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
current: {
|
||||
value: v.current.value,
|
||||
// Text variable doesn't use text state
|
||||
text: v.current.value,
|
||||
},
|
||||
query: v.query,
|
||||
},
|
||||
};
|
||||
variables.push(tx);
|
||||
break;
|
||||
case 'groupby':
|
||||
const gb: GroupByVariableKind = {
|
||||
kind: 'GroupByVariable',
|
||||
spec: {
|
||||
...commonProperties,
|
||||
datasource: v.datasource || getDefaultDatasource(),
|
||||
options: v.options,
|
||||
current: {
|
||||
value: v.current.value,
|
||||
text: v.current.text,
|
||||
},
|
||||
multi: v.multi,
|
||||
},
|
||||
};
|
||||
variables.push(gb);
|
||||
break;
|
||||
default:
|
||||
// do not throw error, just log it
|
||||
console.error(`Variable transformation not implemented: ${v.type}`);
|
||||
@ -447,7 +560,7 @@ function getAnnotations(annotations: AnnotationQuery[]): DashboardV2Spec['annota
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
name: a.name,
|
||||
datasource: a.datasource ?? undefined,
|
||||
...(a.datasource && { datasource: a.datasource }),
|
||||
enable: a.enable,
|
||||
hide: Boolean(a.hide),
|
||||
iconColor: a.iconColor,
|
||||
|
Loading…
Reference in New Issue
Block a user