Dashboard Migrations: V13 no-op, remove 28 and v24 as they are autoMigrations; and remove dead code from DashboardMigrator (#110008)
* migrate to v19 * migrate to v18 * Migration to be verified: v17 Convert minSpan to maxPerRow in panels * Migration to be verified: 16 Grid layout migration * Refactor v17 and v19 migrations to use shared helper functions * Migration to be verified: 15 No-op migration for schema consistency * Migration to be verified: 14 Shared crosshair to graph tooltip migration * cleanup * wip * complete migration * fix lint issues * refactor and test with minimal graph config * update tests * extract defaults outside the func * lint * lint * add missing showValues prop * add context and fix latest version * generate snapshots * v13 should be no-op * clean up * remove v28 * remove singlestat migraiton from frontend migrator because this is an automigration * remove unused function * Remove v24 table plugin logic * cleanup * remove plugin version for automigrate as it was used only in v24 and v28 that have been removed * cleanup --------- Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
This commit is contained in:
co-authored by
Dominik Prokop
parent
5eb295d850
commit
4d3c5d1550
@@ -5,101 +5,186 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestV28SinglestatMigration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input map[string]interface{}
|
||||
expected map[string]interface{}
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "migrate_range_maps_to_field_config_mappings",
|
||||
input: map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"rangeMaps": []interface{}{
|
||||
map[string]interface{}{
|
||||
"from": "null",
|
||||
"to": "N/A",
|
||||
},
|
||||
},
|
||||
"mappingType": 1, // Inconsistent - should be 2 for rangeMaps
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"type": "stat",
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"mappings": []interface{}{
|
||||
map[string]interface{}{
|
||||
"options": map[string]interface{}{
|
||||
"match": "null",
|
||||
"result": map[string]interface{}{
|
||||
"text": "N/A",
|
||||
},
|
||||
},
|
||||
"type": "special",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description: "RangeMaps should migrate to fieldConfig.mappings, and inconsistent mappingType should be fixed to 2 (RangeToText)",
|
||||
},
|
||||
{
|
||||
name: "migrate_sparkline_color_when_color_mode_none",
|
||||
input: map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"colorMode": "None",
|
||||
"sparkline": map[string]interface{}{
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"type": "stat",
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"color": map[string]interface{}{
|
||||
"mode": "fixed",
|
||||
"fixedColor": "rgb(31, 120, 193)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
description: "Sparkline lineColor should migrate to fieldConfig.defaults.color only when colorMode is None",
|
||||
},
|
||||
}
|
||||
type migrationTestCase struct {
|
||||
name string
|
||||
input map[string]interface{}
|
||||
expected map[string]interface{}
|
||||
}
|
||||
|
||||
func runMigrationTests(t *testing.T, tests []migrationTestCase, migrationFunc func(context.Context, map[string]interface{}) error) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dashboard := map[string]interface{}{
|
||||
"schemaVersion": 27,
|
||||
"panels": []interface{}{tt.input},
|
||||
// Create a copy of the input
|
||||
dashboard := make(map[string]interface{})
|
||||
for k, v := range tt.input {
|
||||
dashboard[k] = v
|
||||
}
|
||||
|
||||
err := V28(context.Background(), dashboard)
|
||||
err := migrationFunc(context.Background(), dashboard)
|
||||
if err != nil {
|
||||
t.Fatalf("V28 migration failed: %v", err)
|
||||
t.Fatalf("Migration failed: %v", err)
|
||||
}
|
||||
|
||||
if dashboard["schemaVersion"] != 28 {
|
||||
t.Errorf("Expected schemaVersion to be 28, got %v", dashboard["schemaVersion"])
|
||||
// Verify the result matches expected
|
||||
if !deepEqual(dashboard, tt.expected) {
|
||||
t.Errorf("Migration result doesn't match expected.\nExpected: %+v\nGot: %+v", tt.expected, dashboard)
|
||||
}
|
||||
|
||||
panels, ok := dashboard["panels"].([]interface{})
|
||||
if !ok || len(panels) == 0 {
|
||||
t.Fatalf("Expected panels array with at least one panel")
|
||||
}
|
||||
|
||||
panel, ok := panels[0].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected panel to be a map")
|
||||
}
|
||||
|
||||
// Verify panel type was changed to stat
|
||||
if panel["type"] != "stat" {
|
||||
t.Errorf("Expected panel type to be 'stat', got %v", panel["type"])
|
||||
}
|
||||
|
||||
t.Logf("✓ %s: %s", tt.name, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func deepEqual(a, b interface{}) bool {
|
||||
// Simple deep comparison for test purposes
|
||||
// This is a simplified version - in production you'd use reflect.DeepEqual or similar
|
||||
switch aVal := a.(type) {
|
||||
case map[string]interface{}:
|
||||
bVal, ok := b.(map[string]interface{})
|
||||
if !ok || len(aVal) != len(bVal) {
|
||||
return false
|
||||
}
|
||||
for k, v := range aVal {
|
||||
if !deepEqual(v, bVal[k]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case []interface{}:
|
||||
bVal, ok := b.([]interface{})
|
||||
if !ok || len(aVal) != len(bVal) {
|
||||
return false
|
||||
}
|
||||
for i, v := range aVal {
|
||||
if !deepEqual(v, bVal[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return a == b
|
||||
}
|
||||
}
|
||||
|
||||
func TestV28(t *testing.T) {
|
||||
tests := []migrationTestCase{
|
||||
{
|
||||
name: "v28 removes deprecated variable properties",
|
||||
input: map[string]interface{}{
|
||||
"title": "V28 Variable Properties Migration Test Dashboard",
|
||||
"schemaVersion": 27,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "var1",
|
||||
"tags": []interface{}{"tag1", "tag2"},
|
||||
"tagsQuery": "query_string",
|
||||
"tagValuesQuery": "values_query",
|
||||
"useTags": true,
|
||||
"type": "query",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "var2",
|
||||
"tags": []interface{}{},
|
||||
"tagsQuery": "", // Empty string should not be removed
|
||||
"tagValuesQuery": "", // Empty string should not be removed
|
||||
"useTags": false, // False should not be removed
|
||||
"type": "custom",
|
||||
},
|
||||
},
|
||||
},
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel (unchanged by v28)",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V28 Variable Properties Migration Test Dashboard",
|
||||
"schemaVersion": 28,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "var1",
|
||||
"type": "query",
|
||||
// tags, tagsQuery, tagValuesQuery, useTags should be removed
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "var2",
|
||||
"tagsQuery": "", // Empty string preserved
|
||||
"tagValuesQuery": "", // Empty string preserved
|
||||
"useTags": false, // False preserved
|
||||
"type": "custom",
|
||||
// only tags should be removed
|
||||
},
|
||||
},
|
||||
},
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel (unchanged by v28)",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v28 handles dashboard without templating",
|
||||
input: map[string]interface{}{
|
||||
"title": "Dashboard without templating",
|
||||
"schemaVersion": 27,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "Dashboard without templating",
|
||||
"schemaVersion": 28,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v28 handles empty templating list",
|
||||
input: map[string]interface{}{
|
||||
"title": "Dashboard with empty templating",
|
||||
"schemaVersion": 27,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
},
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "Dashboard with empty templating",
|
||||
"schemaVersion": 28,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
},
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat Panel",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runMigrationTests(t, tests, V28)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user