mirror of https://github.com/grafana/grafana.git
137 lines
4.3 KiB
Go
137 lines
4.3 KiB
Go
package schemaversion
|
|
|
|
import "context"
|
|
|
|
// V31 adds a merge transformer after any labelsToFields transformer in panel transformations.
|
|
//
|
|
// This migration addresses data processing workflow optimization by automatically inserting
|
|
// a merge transformation after the labelsToFields transformation. When the labelsToFields
|
|
// transformer converts time series labels to individual fields, it can result in multiple
|
|
// data frames that need to be consolidated for proper visualization and analysis.
|
|
//
|
|
// The migration works by:
|
|
// 1. Iterating through all panels in the dashboard, including nested panels in collapsed rows
|
|
// 2. Examining the transformations array within each panel
|
|
// 3. Identifying transformations with id 'labelsToFields'
|
|
// 4. Inserting a merge transformation with empty options immediately after each labelsToFields transformation
|
|
// 5. Preserving the original labelsToFields options (mode, keepLabels, valueLabel, etc.) without modification
|
|
// 6. Using empty options for merge transformations to enable optimal default consolidation behavior
|
|
// 7. Preserving the original order and configuration of all other transformations
|
|
//
|
|
// The migration handles complex scenarios including:
|
|
// - Multiple labelsToFields transformations within a single panel
|
|
// - Panels with mixed transformation types
|
|
// - Nested panels within collapsed row panels
|
|
// - Panels with no transformations (left unchanged)
|
|
// - Panels with transformations but no labelsToFields (left unchanged)
|
|
//
|
|
// Example transformation:
|
|
//
|
|
// Before migration:
|
|
//
|
|
// panel: {
|
|
// "transformations": [
|
|
// { "id": "organize", "options": {} },
|
|
// { "id": "labelsToFields", "options": {} },
|
|
// { "id": "calculateField", "options": {} },
|
|
// { "id": "labelsToFields", "options": { "mode": "rows", "keepLabels": ["job", "instance"] } },
|
|
// ]
|
|
// }
|
|
//
|
|
// After migration:
|
|
//
|
|
// panel: {
|
|
// "transformations": [
|
|
// { "id": "organize", "options": {} },
|
|
// { "id": "labelsToFields", "options": {} },
|
|
// { "id": "merge", "options": {} },
|
|
// { "id": "calculateField", "options": {} },
|
|
// { "id": "labelsToFields", "options": { "mode": "rows", "keepLabels": ["job", "instance"] } },
|
|
// { "id": "merge", "options": {} }
|
|
// ]
|
|
// }
|
|
func V31(_ context.Context, dashboard map[string]interface{}) error {
|
|
dashboard["schemaVersion"] = int(31)
|
|
|
|
panels, ok := dashboard["panels"].([]interface{})
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
// Process all panels, including nested ones
|
|
processPanelsV31(panels)
|
|
|
|
return nil
|
|
}
|
|
|
|
// processPanelsV31 recursively processes panels, including nested panels within rows
|
|
func processPanelsV31(panels []interface{}) {
|
|
for _, panel := range panels {
|
|
p, ok := panel.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Process nested panels if this is a row panel
|
|
if p["type"] == "row" {
|
|
nestedPanels, hasNested := p["panels"].([]interface{})
|
|
if !hasNested {
|
|
continue
|
|
}
|
|
processPanelsV31(nestedPanels)
|
|
continue
|
|
}
|
|
|
|
transformations, ok := p["transformations"].([]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Check if we have any labelsToFields transformations
|
|
hasLabelsToFields := false
|
|
for _, transformation := range transformations {
|
|
t, ok := transformation.(map[string]interface{})
|
|
if !ok {
|
|
continue
|
|
}
|
|
if t["id"] == "labelsToFields" {
|
|
hasLabelsToFields = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasLabelsToFields {
|
|
continue
|
|
}
|
|
|
|
// Create new transformations array with merge transformations added
|
|
newTransformations := []interface{}{}
|
|
|
|
for _, transformation := range transformations {
|
|
t, ok := transformation.(map[string]interface{})
|
|
if !ok {
|
|
newTransformations = append(newTransformations, transformation)
|
|
continue
|
|
}
|
|
|
|
// Add the current transformation (preserving all original options)
|
|
newTransformations = append(newTransformations, transformation)
|
|
|
|
// If this is a labelsToFields transformation, add a merge transformation after it
|
|
// with empty options to enable optimal default consolidation behavior
|
|
if t["id"] == "labelsToFields" {
|
|
mergeTransformation := map[string]interface{}{
|
|
"id": "merge",
|
|
"options": map[string]interface{}{},
|
|
}
|
|
newTransformations = append(newTransformations, mergeTransformation)
|
|
}
|
|
}
|
|
|
|
// Update the panel with the new transformations - only if not empty
|
|
if len(newTransformations) > 0 {
|
|
p["transformations"] = newTransformations
|
|
}
|
|
}
|
|
}
|