Files
grafana/apps/dashboard/pkg/migration/schemaversion/v17.go
2025-09-03 20:41:37 +00:00

127 lines
2.8 KiB
Go

package schemaversion
import (
"context"
"math"
"sort"
)
// V17 migrates panel minSpan property to maxPerRow property.
// This migration converts the deprecated minSpan property to the newer maxPerRow property
// which controls how many panels can be displayed in a single row.
//
// The conversion algorithm:
// 1. Calculate max = GRID_COLUMN_COUNT / panel.minSpan
// 2. Get all factors of GRID_COLUMN_COUNT (24): [1, 2, 3, 4, 6, 8, 12, 24]
// 3. Find the first factor greater than max
// 4. Use the previous factor as maxPerRow
//
// Example before migration:
//
// "panels": [
// {
// "id": 1,
// "type": "graph",
// "minSpan": 8
// }
// ]
//
// Example after migration:
//
// "panels": [
// {
// "id": 1,
// "type": "graph",
// "maxPerRow": 3
// }
// ]
//
// The minSpan property is removed after conversion.
func V17(_ context.Context, dashboard map[string]interface{}) error {
dashboard["schemaVersion"] = 17
panels, ok := dashboard["panels"].([]interface{})
if !ok {
return nil
}
for _, p := range panels {
panel, ok := p.(map[string]interface{})
if !ok {
continue
}
migrateMinSpanToMaxPerRow(panel)
}
return nil
}
// migrateMinSpanToMaxPerRow converts minSpan to maxPerRow using the same algorithm as the frontend.
func migrateMinSpanToMaxPerRow(panel map[string]interface{}) {
minSpanValue, ok := panel["minSpan"]
if !ok {
return
}
// Convert minSpan to a number using shared utility
minSpan, ok := ConvertToFloat(minSpanValue)
if !ok {
// If we can't convert minSpan to a number, just delete it and return
delete(panel, "minSpan")
return
}
// Ensure minSpan is positive to avoid division by zero
if minSpan <= 0 {
delete(panel, "minSpan")
return
}
const gridColumnCount = 24
max := gridColumnCount / minSpan
factors := getFactors(gridColumnCount)
// Find the first factor greater than max
factorIndex := -1
for i, factor := range factors {
if float64(factor) > max {
factorIndex = i
break
}
}
// Use the previous factor as maxPerRow
if factorIndex > 0 {
panel["maxPerRow"] = factors[factorIndex-1]
} else if factorIndex == 0 {
// If the first factor is already greater than max, use 1
panel["maxPerRow"] = 1
} else {
// If no factor is greater than max, use the largest factor
panel["maxPerRow"] = factors[len(factors)-1]
}
// Remove the minSpan property
delete(panel, "minSpan")
}
// getFactors returns all factors of a number
// Example: getFactors(24) returns [1, 2, 3, 4, 6, 8, 12, 24]
func getFactors(num int) []int {
factors := []int{}
for i := 1; i <= int(math.Sqrt(float64(num))); i++ {
if num%i == 0 {
factors = append(factors, i)
if i != num/i {
factors = append(factors, num/i)
}
}
}
// Sort factors in ascending order
sort.Ints(factors)
return factors
}