Fix v38 table panel overrides migration when defaults.custom is missing (#112430)
Dashboard Migration: Fix v38 table panel overrides migration when defaults.custom is missing The v38 migration was incorrectly skipping field config overrides processing when fieldConfig.defaults.custom didn't exist. This caused custom.displayMode properties in overrides to not be migrated to the new custom.cellOptions format. The fix ensures migrateOverrides is always called for table panels, regardless of whether defaults.custom exists. Added comprehensive unit test covering this edge case to prevent regression. Issue discovered through comprehensive migration testing infrastructure.
This commit is contained in:
@@ -112,26 +112,22 @@ func processPanelsV38(panels []interface{}) {
|
||||
continue
|
||||
}
|
||||
|
||||
defaults, ok := fieldConfig["defaults"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
custom, ok := defaults["custom"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Migrate displayMode to cellOptions
|
||||
if displayMode, exists := custom["displayMode"]; exists {
|
||||
if displayModeStr, ok := displayMode.(string); ok {
|
||||
custom["cellOptions"] = migrateTableDisplayModeToCellOptions(displayModeStr)
|
||||
// Process defaults.custom if it exists
|
||||
if defaults, ok := fieldConfig["defaults"].(map[string]interface{}); ok {
|
||||
if custom, ok := defaults["custom"].(map[string]interface{}); ok {
|
||||
// Migrate displayMode to cellOptions in defaults
|
||||
if displayMode, exists := custom["displayMode"]; exists {
|
||||
if displayModeStr, ok := displayMode.(string); ok {
|
||||
custom["cellOptions"] = migrateTableDisplayModeToCellOptions(displayModeStr)
|
||||
}
|
||||
// Delete the legacy field
|
||||
delete(custom, "displayMode")
|
||||
}
|
||||
}
|
||||
// Delete the legacy field
|
||||
delete(custom, "displayMode")
|
||||
}
|
||||
|
||||
// Update any overrides referencing the cell display mode
|
||||
// This must be called regardless of whether defaults.custom exists
|
||||
migrateOverrides(fieldConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +440,164 @@ func TestV38(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "table with missing defaults.custom but overrides with custom.displayMode",
|
||||
input: map[string]interface{}{
|
||||
"title": "V38 Missing Defaults Custom Test",
|
||||
"schemaVersion": 37,
|
||||
"panels": []interface{}{
|
||||
// Table with no custom in defaults but custom.displayMode in overrides
|
||||
map[string]interface{}{
|
||||
"type": "table",
|
||||
"title": "Table with Missing Defaults Custom",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{}, // No custom object
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "name",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "color",
|
||||
"value": map[string]interface{}{
|
||||
"fixedColor": "yellow",
|
||||
"mode": "fixed",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "custom.displayMode",
|
||||
"value": "color-background",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Table with empty custom in defaults but custom.displayMode in overrides
|
||||
map[string]interface{}{
|
||||
"type": "table",
|
||||
"title": "Table with Empty Custom",
|
||||
"id": 2,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"custom": map[string]interface{}{}, // Empty custom object
|
||||
},
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "status",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "custom.displayMode",
|
||||
"value": "gradient-gauge",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "value",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "custom.displayMode",
|
||||
"value": "lcd-gauge",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V38 Missing Defaults Custom Test",
|
||||
"schemaVersion": 38,
|
||||
"panels": []interface{}{
|
||||
// Table with no custom in defaults but custom.displayMode in overrides (migrated)
|
||||
map[string]interface{}{
|
||||
"type": "table",
|
||||
"title": "Table with Missing Defaults Custom",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{}, // Still no custom object
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "name",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "color",
|
||||
"value": map[string]interface{}{
|
||||
"fixedColor": "yellow",
|
||||
"mode": "fixed",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "custom.cellOptions",
|
||||
"value": map[string]interface{}{
|
||||
"type": "color-background",
|
||||
"mode": "gradient",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Table with empty custom in defaults but custom.displayMode in overrides (migrated)
|
||||
map[string]interface{}{
|
||||
"type": "table",
|
||||
"title": "Table with Empty Custom",
|
||||
"id": 2,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"custom": map[string]interface{}{}, // Empty custom object
|
||||
},
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "status",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "custom.cellOptions",
|
||||
"value": map[string]interface{}{
|
||||
"type": "gauge",
|
||||
"mode": "gradient",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "value",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "custom.cellOptions",
|
||||
"value": map[string]interface{}{
|
||||
"type": "gauge",
|
||||
"mode": "lcd",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
runMigrationTests(t, tests, schemaversion.V38)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user