Dashboards: Cover the Switch variable in schema transformations - part 2. (#114549)
* feat: add v2alpha_1 conversion for the switch variable * chore: gofmt fixes * chore: update comments in tests * chore: fix gofmt * Update specs * tests: update the v2alpha1 openapi snapshot --------- Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
+26
@@ -258,6 +258,32 @@
|
||||
"multi": true,
|
||||
"skipUrlSync": false
|
||||
},
|
||||
{
|
||||
"name": "switch_var",
|
||||
"type": "switch",
|
||||
"label": "Enable Feature",
|
||||
"description": "Toggle feature on/off",
|
||||
"query": "",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "false",
|
||||
"value": "false"
|
||||
},
|
||||
"options": [
|
||||
{
|
||||
"selected": false,
|
||||
"text": "true",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"selected": true,
|
||||
"text": "false",
|
||||
"value": "false"
|
||||
}
|
||||
],
|
||||
"hide": 0,
|
||||
"skipUrlSync": false
|
||||
},
|
||||
{
|
||||
"name": "legacy_string_var",
|
||||
"type": "query",
|
||||
|
||||
Vendored
+26
@@ -277,6 +277,32 @@
|
||||
"skipUrlSync": false,
|
||||
"type": "groupby"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "false",
|
||||
"value": "false"
|
||||
},
|
||||
"description": "Toggle feature on/off",
|
||||
"hide": 0,
|
||||
"label": "Enable Feature",
|
||||
"name": "switch_var",
|
||||
"options": [
|
||||
{
|
||||
"selected": false,
|
||||
"text": "true",
|
||||
"value": "true"
|
||||
},
|
||||
{
|
||||
"selected": true,
|
||||
"text": "false",
|
||||
"value": "false"
|
||||
}
|
||||
],
|
||||
"query": "",
|
||||
"skipUrlSync": false,
|
||||
"type": "switch"
|
||||
},
|
||||
{
|
||||
"current": {
|
||||
"selected": false,
|
||||
|
||||
Vendored
+13
@@ -317,6 +317,19 @@
|
||||
"description": "Group results by field"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "SwitchVariable",
|
||||
"spec": {
|
||||
"name": "switch_var",
|
||||
"current": "false",
|
||||
"enabledValue": "true",
|
||||
"disabledValue": "false",
|
||||
"label": "Enable Feature",
|
||||
"hide": "dontHide",
|
||||
"skipUrlSync": false,
|
||||
"description": "Toggle feature on/off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "QueryVariable",
|
||||
"spec": {
|
||||
|
||||
Vendored
+13
@@ -318,6 +318,19 @@
|
||||
"description": "Group results by field"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "SwitchVariable",
|
||||
"spec": {
|
||||
"name": "switch_var",
|
||||
"current": "false",
|
||||
"enabledValue": "true",
|
||||
"disabledValue": "false",
|
||||
"label": "Enable Feature",
|
||||
"hide": "dontHide",
|
||||
"skipUrlSync": false,
|
||||
"description": "Toggle feature on/off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "QueryVariable",
|
||||
"spec": {
|
||||
|
||||
@@ -956,6 +956,10 @@ func transformVariables(ctx context.Context, dashboard map[string]interface{}, d
|
||||
if textVar, err := buildTextVariable(varMap, commonProps); err == nil {
|
||||
variables = append(variables, textVar)
|
||||
}
|
||||
case "switch":
|
||||
if switchVar, err := buildSwitchVariable(varMap, commonProps); err == nil {
|
||||
variables = append(variables, switchVar)
|
||||
}
|
||||
case "groupby":
|
||||
if groupByVar, err := buildGroupByVariable(ctx, varMap, commonProps, dsIndexProvider); err == nil {
|
||||
variables = append(variables, groupByVar)
|
||||
@@ -1415,6 +1419,75 @@ func buildTextVariable(varMap map[string]interface{}, commonProps CommonVariable
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper function to extract string value from an option map (value or text field)
|
||||
func getOptionValue(optMap map[string]interface{}) string {
|
||||
if val, ok := optMap["value"].(string); ok && val != "" {
|
||||
return val
|
||||
}
|
||||
if val, ok := optMap["text"].(string); ok && val != "" {
|
||||
return val
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Switch Variable
|
||||
func buildSwitchVariable(varMap map[string]interface{}, commonProps CommonVariableProperties) (dashv2alpha1.DashboardVariableKind, error) {
|
||||
current := ""
|
||||
if currentVal, exists := varMap["current"]; exists {
|
||||
if currentMap, ok := currentVal.(map[string]interface{}); ok {
|
||||
current = getOptionValue(currentMap)
|
||||
}
|
||||
}
|
||||
|
||||
// In V1 the enabled value is the first value of the options array,
|
||||
// while the disabled value is second one.
|
||||
// (Falling back to "true" and "false" if options are not available)
|
||||
enabledValue := "true"
|
||||
disabledValue := "false"
|
||||
|
||||
if options, ok := varMap["options"].([]interface{}); ok {
|
||||
// Get enabledValue from first option
|
||||
if len(options) > 0 {
|
||||
if opt1, ok := options[0].(map[string]interface{}); ok {
|
||||
if val := getOptionValue(opt1); val != "" {
|
||||
enabledValue = val
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get disabledValue from second option
|
||||
if len(options) > 1 {
|
||||
if opt2, ok := options[1].(map[string]interface{}); ok {
|
||||
if val := getOptionValue(opt2); val != "" {
|
||||
disabledValue = val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set current to disabledValue if not set
|
||||
if current == "" {
|
||||
current = disabledValue
|
||||
}
|
||||
|
||||
switchVar := &dashv2alpha1.DashboardSwitchVariableKind{
|
||||
Kind: "SwitchVariable",
|
||||
Spec: dashv2alpha1.DashboardSwitchVariableSpec{
|
||||
Name: commonProps.Name,
|
||||
Current: current,
|
||||
EnabledValue: enabledValue,
|
||||
DisabledValue: disabledValue,
|
||||
Label: commonProps.Label,
|
||||
Description: commonProps.Description,
|
||||
Hide: commonProps.Hide,
|
||||
SkipUrlSync: commonProps.SkipUrlSync,
|
||||
},
|
||||
}
|
||||
|
||||
return dashv2alpha1.DashboardVariableKind{
|
||||
SwitchVariableKind: switchVar,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Adhoc Variable
|
||||
func buildAdhocVariable(ctx context.Context, varMap map[string]interface{}, commonProps CommonVariableProperties, dsIndexProvider schemaversion.DataSourceIndexProvider) (dashv2alpha1.DashboardVariableKind, error) {
|
||||
datasource := varMap["datasource"]
|
||||
|
||||
@@ -733,6 +733,22 @@ func convertVariable_V2alpha1_to_V2beta1(in *dashv2alpha1.DashboardVariableKind,
|
||||
}
|
||||
}
|
||||
|
||||
if in.SwitchVariableKind != nil {
|
||||
out.SwitchVariableKind = &dashv2beta1.DashboardSwitchVariableKind{
|
||||
Kind: in.SwitchVariableKind.Kind,
|
||||
Spec: dashv2beta1.DashboardSwitchVariableSpec{
|
||||
Name: in.SwitchVariableKind.Spec.Name,
|
||||
Current: in.SwitchVariableKind.Spec.Current,
|
||||
EnabledValue: in.SwitchVariableKind.Spec.EnabledValue,
|
||||
DisabledValue: in.SwitchVariableKind.Spec.DisabledValue,
|
||||
Label: in.SwitchVariableKind.Spec.Label,
|
||||
Hide: dashv2beta1.DashboardVariableHide(in.SwitchVariableKind.Spec.Hide),
|
||||
SkipUrlSync: in.SwitchVariableKind.Spec.SkipUrlSync,
|
||||
Description: in.SwitchVariableKind.Spec.Description,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
|
||||
migrationtestutil "github.com/grafana/grafana/apps/dashboard/pkg/migration/testutil"
|
||||
)
|
||||
|
||||
// TestV2alpha1ToV2beta1 tests the conversion logic for v2alpha1 to v2beta1.
|
||||
func TestV2alpha1ToV2beta1(t *testing.T) {
|
||||
// Initialize the migrator with test providers
|
||||
dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig)
|
||||
leProvider := migrationtestutil.NewLibraryElementProvider()
|
||||
migration.Initialize(dsProvider, leProvider)
|
||||
|
||||
// Set up conversion scheme
|
||||
scheme := runtime.NewScheme()
|
||||
err := RegisterConversions(scheme, dsProvider, leProvider)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
createV2alpha1 func() *dashv2alpha1.Dashboard
|
||||
validateV2beta1 func(t *testing.T, v2beta1 *dashv2beta1.Dashboard)
|
||||
}{
|
||||
{
|
||||
name: "dashboard with switch variable",
|
||||
createV2alpha1: func() *dashv2alpha1.Dashboard {
|
||||
label := "Enable Feature"
|
||||
description := "Toggle feature"
|
||||
return &dashv2alpha1.Dashboard{
|
||||
Spec: dashv2alpha1.DashboardSpec{
|
||||
Title: "Test Dashboard",
|
||||
Variables: []dashv2alpha1.DashboardVariableKind{
|
||||
{
|
||||
SwitchVariableKind: &dashv2alpha1.DashboardSwitchVariableKind{
|
||||
Kind: "SwitchVariable",
|
||||
Spec: dashv2alpha1.DashboardSwitchVariableSpec{
|
||||
Name: "switch_var",
|
||||
Current: "false",
|
||||
EnabledValue: "true",
|
||||
DisabledValue: "false",
|
||||
Label: &label,
|
||||
Description: &description,
|
||||
Hide: dashv2alpha1.DashboardVariableHideDontHide,
|
||||
SkipUrlSync: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validateV2beta1: func(t *testing.T, v2beta1 *dashv2beta1.Dashboard) {
|
||||
require.Len(t, v2beta1.Spec.Variables, 1)
|
||||
variable := v2beta1.Spec.Variables[0]
|
||||
require.NotNil(t, variable.SwitchVariableKind, "SwitchVariableKind should not be nil")
|
||||
assert.Equal(t, "SwitchVariable", variable.SwitchVariableKind.Kind)
|
||||
assert.Equal(t, "switch_var", variable.SwitchVariableKind.Spec.Name)
|
||||
assert.Equal(t, "false", variable.SwitchVariableKind.Spec.Current)
|
||||
assert.Equal(t, "true", variable.SwitchVariableKind.Spec.EnabledValue)
|
||||
assert.Equal(t, "false", variable.SwitchVariableKind.Spec.DisabledValue)
|
||||
assert.NotNil(t, variable.SwitchVariableKind.Spec.Label)
|
||||
assert.Equal(t, "Enable Feature", *variable.SwitchVariableKind.Spec.Label)
|
||||
assert.NotNil(t, variable.SwitchVariableKind.Spec.Description)
|
||||
assert.Equal(t, "Toggle feature", *variable.SwitchVariableKind.Spec.Description)
|
||||
assert.Equal(t, dashv2beta1.DashboardVariableHideDontHide, variable.SwitchVariableKind.Spec.Hide)
|
||||
assert.False(t, variable.SwitchVariableKind.Spec.SkipUrlSync)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with switch variable - custom values for enabled and disable states",
|
||||
createV2alpha1: func() *dashv2alpha1.Dashboard {
|
||||
label := "Enable Feature"
|
||||
description := "Toggle feature"
|
||||
return &dashv2alpha1.Dashboard{
|
||||
Spec: dashv2alpha1.DashboardSpec{
|
||||
Title: "Test Dashboard",
|
||||
Variables: []dashv2alpha1.DashboardVariableKind{
|
||||
{
|
||||
SwitchVariableKind: &dashv2alpha1.DashboardSwitchVariableKind{
|
||||
Kind: "SwitchVariable",
|
||||
Spec: dashv2alpha1.DashboardSwitchVariableSpec{
|
||||
Name: "switch_var",
|
||||
Current: "true",
|
||||
EnabledValue: "enabled",
|
||||
DisabledValue: "disabled",
|
||||
Label: &label,
|
||||
Description: &description,
|
||||
Hide: dashv2alpha1.DashboardVariableHideHideLabel,
|
||||
SkipUrlSync: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validateV2beta1: func(t *testing.T, v2beta1 *dashv2beta1.Dashboard) {
|
||||
require.Len(t, v2beta1.Spec.Variables, 1)
|
||||
variable := v2beta1.Spec.Variables[0]
|
||||
require.NotNil(t, variable.SwitchVariableKind)
|
||||
assert.Equal(t, "switch_var", variable.SwitchVariableKind.Spec.Name)
|
||||
assert.Equal(t, "true", variable.SwitchVariableKind.Spec.Current)
|
||||
assert.Equal(t, "enabled", variable.SwitchVariableKind.Spec.EnabledValue)
|
||||
assert.Equal(t, "disabled", variable.SwitchVariableKind.Spec.DisabledValue)
|
||||
assert.Equal(t, dashv2beta1.DashboardVariableHideHideLabel, variable.SwitchVariableKind.Spec.Hide)
|
||||
assert.True(t, variable.SwitchVariableKind.Spec.SkipUrlSync)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create v2alpha1 dashboard
|
||||
v2alpha1 := tc.createV2alpha1()
|
||||
|
||||
// Collect original statistics
|
||||
originalStats := collectStatsV2alpha1(v2alpha1.Spec)
|
||||
|
||||
// Convert to v2beta1
|
||||
var v2beta1 dashv2beta1.Dashboard
|
||||
err := scheme.Convert(v2alpha1, &v2beta1, nil)
|
||||
require.NoError(t, err, "Failed to convert v2alpha1 to v2beta1")
|
||||
|
||||
// Collect v2beta1 statistics
|
||||
v2beta1Stats := collectStatsV2beta1(v2beta1.Spec)
|
||||
|
||||
// Verify no data loss
|
||||
err = detectConversionDataLoss(originalStats, v2beta1Stats, "V2alpha1", "V2beta1")
|
||||
assert.NoError(t, err, "Data loss detected in conversion")
|
||||
|
||||
// Run custom validation
|
||||
tc.validateV2beta1(t, &v2beta1)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -771,6 +771,23 @@ func convertVariable_V2beta1_to_V2alpha1(in *dashv2beta1.DashboardVariableKind,
|
||||
}
|
||||
}
|
||||
|
||||
if in.SwitchVariableKind != nil {
|
||||
out.SwitchVariableKind = &dashv2alpha1.DashboardSwitchVariableKind{
|
||||
Kind: in.SwitchVariableKind.Kind,
|
||||
Spec: dashv2alpha1.DashboardSwitchVariableSpec{
|
||||
Name: in.SwitchVariableKind.Spec.Name,
|
||||
Current: in.SwitchVariableKind.Spec.Current,
|
||||
EnabledValue: in.SwitchVariableKind.Spec.EnabledValue,
|
||||
DisabledValue: in.SwitchVariableKind.Spec.DisabledValue,
|
||||
Label: in.SwitchVariableKind.Spec.Label,
|
||||
Hide: dashv2alpha1.DashboardVariableHide(in.SwitchVariableKind.Spec.Hide),
|
||||
SkipUrlSync: in.SwitchVariableKind.Spec.SkipUrlSync,
|
||||
Description: in.SwitchVariableKind.Spec.Description,
|
||||
},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -479,6 +479,51 @@ func TestV2beta1ToV2alpha1(t *testing.T) {
|
||||
assert.Equal(t, "", variable.QueryVariableKind.Spec.Query.Kind, "Empty group should result in empty kind")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with switch variable",
|
||||
createV2beta1: func() *dashv2beta1.Dashboard {
|
||||
label := "Enable Feature"
|
||||
description := "Toggle feature"
|
||||
return &dashv2beta1.Dashboard{
|
||||
Spec: dashv2beta1.DashboardSpec{
|
||||
Title: "Test Dashboard",
|
||||
Variables: []dashv2beta1.DashboardVariableKind{
|
||||
{
|
||||
SwitchVariableKind: &dashv2beta1.DashboardSwitchVariableKind{
|
||||
Kind: "SwitchVariable",
|
||||
Spec: dashv2beta1.DashboardSwitchVariableSpec{
|
||||
Name: "switch_var",
|
||||
Current: "false",
|
||||
EnabledValue: "true",
|
||||
DisabledValue: "false",
|
||||
Label: &label,
|
||||
Description: &description,
|
||||
Hide: dashv2beta1.DashboardVariableHideDontHide,
|
||||
SkipUrlSync: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
validateV2alpha1: func(t *testing.T, v2alpha1 *dashv2alpha1.Dashboard) {
|
||||
require.Len(t, v2alpha1.Spec.Variables, 1)
|
||||
variable := v2alpha1.Spec.Variables[0]
|
||||
require.NotNil(t, variable.SwitchVariableKind, "SwitchVariableKind should not be nil")
|
||||
assert.Equal(t, "SwitchVariable", variable.SwitchVariableKind.Kind)
|
||||
assert.Equal(t, "switch_var", variable.SwitchVariableKind.Spec.Name)
|
||||
assert.Equal(t, "false", variable.SwitchVariableKind.Spec.Current)
|
||||
assert.Equal(t, "true", variable.SwitchVariableKind.Spec.EnabledValue)
|
||||
assert.Equal(t, "false", variable.SwitchVariableKind.Spec.DisabledValue)
|
||||
assert.NotNil(t, variable.SwitchVariableKind.Spec.Label)
|
||||
assert.Equal(t, "Enable Feature", *variable.SwitchVariableKind.Spec.Label)
|
||||
assert.NotNil(t, variable.SwitchVariableKind.Spec.Description)
|
||||
assert.Equal(t, "Toggle feature", *variable.SwitchVariableKind.Spec.Description)
|
||||
assert.Equal(t, dashv2alpha1.DashboardVariableHideDontHide, variable.SwitchVariableKind.Spec.Hide)
|
||||
assert.False(t, variable.SwitchVariableKind.Spec.SkipUrlSync)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with rows layout",
|
||||
createV2beta1: func() *dashv2beta1.Dashboard {
|
||||
|
||||
Reference in New Issue
Block a user