Dashboards: Cover the Switch variable in schema transformations - part 1. (#114293)

fix: cover the switch variable when transforming betwen v1 and v2 schemas
This commit is contained in:
Levente Balogh
2025-11-27 15:14:42 +01:00
committed by GitHub
parent 80fc87339a
commit 8e73cc2f70
4 changed files with 118 additions and 1 deletions
@@ -490,5 +490,18 @@ export const handyTestingSchema: Spec = {
allowCustomValue: true,
},
},
{
kind: 'SwitchVariable',
spec: {
name: 'switchVar',
label: 'Switch Variable',
description: 'A switch variable',
current: 'false',
enabledValue: 'true',
disabledValue: 'false',
hide: 'dontHide',
skipUrlSync: false,
},
},
],
};
@@ -14,6 +14,7 @@ import {
AdHocFiltersVariable,
SceneDataTransformer,
SceneGridItem,
SwitchVariable,
} from '@grafana/scenes';
import {
AdhocVariableKind,
@@ -27,6 +28,7 @@ import {
GroupByVariableKind,
IntervalVariableKind,
QueryVariableKind,
SwitchVariableKind,
TextVariableKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2';
import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2_examples';
@@ -204,6 +206,14 @@ describe('transformSaveModelSchemaV2ToScene', () => {
sceneVariableClass: AdHocFiltersVariable,
index: 7,
});
validateVariable({
sceneVariable: variables?.state.variables[8],
variableKind: dash.variables[8] as SwitchVariableKind,
scene: scene,
dashSpec: dash,
sceneVariableClass: SwitchVariable,
index: 8,
});
// Annotations
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
@@ -371,7 +381,7 @@ describe('transformSaveModelSchemaV2ToScene', () => {
const scene = transformSaveModelSchemaV2ToScene(snapshot);
// check variables were converted to snapshot variables
expect(scene.state.$variables?.state.variables).toHaveLength(8);
expect(scene.state.$variables?.state.variables).toHaveLength(9);
expect(scene.state.$variables?.getByName('customVar')).toBeInstanceOf(SnapshotVariable);
expect(scene.state.$variables?.getByName('adhocVar')).toBeInstanceOf(AdHocFiltersVariable);
expect(scene.state.$variables?.getByName('intervalVar')).toBeInstanceOf(SnapshotVariable);
@@ -311,6 +311,31 @@ describe('ResponseTransformers', () => {
type: 'query',
query: { refId: 'A', query: 'label_values(grafanacloud_org_info{org_slug="$org_slug"}, org_id)' },
},
{
type: 'switch',
name: 'var9',
label: 'Switch variable',
description: 'Switch variable description',
skipUrlSync: false,
hide: 0,
current: {
value: 'true',
text: 'true',
},
options: [
{
selected: true,
text: 'true',
value: 'true',
},
{
selected: false,
text: 'false',
value: 'false',
},
],
query: '',
},
],
},
panels: [
@@ -523,6 +548,7 @@ describe('ResponseTransformers', () => {
validateVariablesV1ToV2(spec.variables[6], dashboardV1.templating?.list?.[6]);
validateVariablesV1ToV2(spec.variables[7], dashboardV1.templating?.list?.[7]);
validateVariablesV1ToV2(spec.variables[8], dashboardV1.templating?.list?.[8]);
validateVariablesV1ToV2(spec.variables[9], dashboardV1.templating?.list?.[9]);
});
});
@@ -930,6 +956,7 @@ describe('ResponseTransformers', () => {
validateVariablesV1ToV2(dashboardV2.spec.variables[5], dashboard.templating?.list?.[5]);
validateVariablesV1ToV2(dashboardV2.spec.variables[6], dashboard.templating?.list?.[6]);
validateVariablesV1ToV2(dashboardV2.spec.variables[7], dashboard.templating?.list?.[7]);
validateVariablesV1ToV2(dashboardV2.spec.variables[8], dashboard.templating?.list?.[8]);
// annotations
validateAnnotation(dashboard.annotations!.list![0], dashboardV2.spec.annotations[0]);
validateAnnotation(dashboard.annotations!.list![1], dashboardV2.spec.annotations[1]);
@@ -1172,5 +1199,23 @@ describe('ResponseTransformers', () => {
expect(v2.group).toEqual(v1.datasource?.type);
expect(v2.spec.options).toEqual(v1.options);
}
if (v2.kind === 'SwitchVariable') {
// V1 switch variables have options array with exactly 2 options
// First option is enabledValue, second is disabledValue
const options = v1.options ?? [];
const enabledValueRaw = options[0]?.value ?? 'true';
const disabledValueRaw = options[1]?.value ?? 'false';
const enabledValue = Array.isArray(enabledValueRaw) ? enabledValueRaw[0] : enabledValueRaw;
const disabledValue = Array.isArray(disabledValueRaw) ? disabledValueRaw[0] : disabledValueRaw;
// Current value should be a string (not array)
const currentValueRaw = v1.current?.value ?? disabledValue;
const currentValue = Array.isArray(currentValueRaw) ? currentValueRaw[0] : currentValueRaw;
expect(v2.spec.current).toBe(currentValue);
expect(v2.spec.enabledValue).toBe(enabledValue);
expect(v2.spec.disabledValue).toBe(disabledValue);
}
}
});
@@ -34,6 +34,7 @@ import {
IntervalVariableKind,
TextVariableKind,
GroupByVariableKind,
SwitchVariableKind,
LibraryPanelKind,
PanelKind,
GridLayoutItemKind,
@@ -809,6 +810,29 @@ function getVariables(vars: TypedVariableModel[]): DashboardV2Spec['variables']
variables.push(gb);
break;
case 'switch':
// V1 switch variables have options array with exactly 2 options
// First option is typically enabledValue, second is disabledValue
const options = v.options ?? [];
const enabledValueRaw = options[0]?.value ?? 'true';
const disabledValueRaw = options[1]?.value ?? 'false';
const enabledValue = Array.isArray(enabledValueRaw) ? enabledValueRaw[0] : enabledValueRaw;
const disabledValue = Array.isArray(disabledValueRaw) ? disabledValueRaw[0] : disabledValueRaw;
// Current value should be a string (not array)
const currentValueRaw = v.current?.value ?? disabledValue;
const currentValue = Array.isArray(currentValueRaw) ? currentValueRaw[0] : currentValueRaw;
const sw: SwitchVariableKind = {
kind: 'SwitchVariable',
spec: {
...commonProperties,
current: currentValue,
enabledValue,
disabledValue,
},
};
variables.push(sw);
break;
default:
// do not throw error, just log it
console.error(`Variable transformation not implemented: ${v.type}`);
@@ -997,6 +1021,29 @@ function getVariablesV1(vars: DashboardV2Spec['variables']): VariableModel[] {
};
variables.push(av);
break;
case 'SwitchVariable':
const sv: VariableModel = {
...commonProperties,
current: {
text: v.spec.current,
value: v.spec.current,
},
options: [
{
text: v.spec.enabledValue,
value: v.spec.enabledValue,
selected: v.spec.current === v.spec.enabledValue,
},
{
text: v.spec.disabledValue,
value: v.spec.disabledValue,
selected: v.spec.current === v.spec.disabledValue,
},
],
query: '',
};
variables.push(sv);
break;
default:
// do not throw error, just log it
console.error(`Variable transformation not implemented: ${v}`);
@@ -1256,6 +1303,8 @@ function transformToV1VariableTypes(variable: TypedVariableModelV2): VariableTyp
return 'groupby';
case 'AdhocVariable':
return 'adhoc';
case 'SwitchVariable':
return 'switch';
default:
throw new Error(`Unknown variable type: ${variable}`);
}