diff --git a/apps/collections/pkg/apis/collections/v1alpha1/stars.go b/apps/collections/pkg/apis/collections/v1alpha1/stars.go index e203e6b560e..79bff8a6513 100644 --- a/apps/collections/pkg/apis/collections/v1alpha1/stars.go +++ b/apps/collections/pkg/apis/collections/v1alpha1/stars.go @@ -8,9 +8,8 @@ import ( func (stars *StarsSpec) Add(group, kind, name string) { for i, r := range stars.Resource { if r.Group == group && r.Kind == kind { - r.Names = append(r.Names, name) - slices.Sort(r.Names) - stars.Resource[i].Names = slices.Compact(r.Names) + stars.Resource[i].Names = append(r.Names, name) + stars.Normalize() return } } @@ -46,8 +45,15 @@ func (stars *StarsSpec) Normalize() { resources := make([]StarsResource, 0, len(stars.Resource)) for _, r := range stars.Resource { if len(r.Names) > 0 { - slices.Sort(r.Names) - r.Names = slices.Compact(r.Names) // removes any duplicates + unique := make([]string, 0, len(r.Names)) + found := make(map[string]bool, len(r.Names)) + for _, name := range r.Names { + if !found[name] { + unique = append(unique, name) + found[name] = true + } + } + r.Names = unique resources = append(resources, r) } } diff --git a/apps/collections/pkg/apis/collections/v1alpha1/stars_test.go b/apps/collections/pkg/apis/collections/v1alpha1/stars_test.go index 5713b07a751..d4421fae614 100644 --- a/apps/collections/pkg/apis/collections/v1alpha1/stars_test.go +++ b/apps/collections/pkg/apis/collections/v1alpha1/stars_test.go @@ -39,7 +39,7 @@ func TestStarsWrite(t *testing.T) { Resource: []StarsResource{{ Group: "g", Kind: "k", - Names: []string{"a", "b", "c", "x"}, // added "b" (and sorted) + Names: []string{"a", "b", "x", "c"}, // added c to the end }}, }, }, { diff --git a/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection.go b/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection.go index 296a95ac054..db3353b66a1 100644 --- a/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection.go +++ b/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection.go @@ -4,8 +4,6 @@ import ( "errors" "fmt" - "k8s.io/apimachinery/pkg/conversion" - dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" @@ -121,6 +119,14 @@ func countPanelsV0V1(spec map[string]interface{}) int { return count } +// countTargetsFromPanel counts the number of targets/queries in a panel. +func countTargetsFromPanel(panelMap map[string]interface{}) int { + if targets, ok := panelMap["targets"].([]interface{}); ok { + return len(targets) + } + return 0 +} + // countQueriesV0V1 counts data queries in v0alpha1 or v1beta1 dashboard spec // Note: Row panels are layout containers and should not have queries. // We ignore any queries on row panels themselves, but count queries in their collapsed panels. @@ -145,9 +151,7 @@ func countQueriesV0V1(spec map[string]interface{}) int { // Count queries in regular panels (NOT row panels) if panelType != "row" { - if targets, ok := panelMap["targets"].([]interface{}); ok { - count += len(targets) - } + count += countTargetsFromPanel(panelMap) } // Count queries in collapsed panels inside row panels @@ -155,9 +159,7 @@ func countQueriesV0V1(spec map[string]interface{}) int { if collapsedPanels, ok := panelMap["panels"].([]interface{}); ok { for _, cp := range collapsedPanels { if cpMap, ok := cp.(map[string]interface{}); ok { - if targets, ok := cpMap["targets"].([]interface{}); ok { - count += len(targets) - } + count += countTargetsFromPanel(cpMap) } } } @@ -442,77 +444,3 @@ func collectDashboardStats(dashboard interface{}) dashboardStats { } return dashboardStats{} } - -// withConversionDataLossDetection wraps a conversion function to detect data loss -func withConversionDataLossDetection(sourceFuncName, targetFuncName string, conversionFunc func(a, b interface{}, scope conversion.Scope) error) func(a, b interface{}, scope conversion.Scope) error { - return func(a, b interface{}, scope conversion.Scope) error { - // Collect source statistics - var sourceStats dashboardStats - switch source := a.(type) { - case *dashv0.Dashboard: - if source.Spec.Object != nil { - sourceStats = collectStatsV0V1(source.Spec.Object) - } - case *dashv1.Dashboard: - if source.Spec.Object != nil { - sourceStats = collectStatsV0V1(source.Spec.Object) - } - case *dashv2alpha1.Dashboard: - sourceStats = collectStatsV2alpha1(source.Spec) - case *dashv2beta1.Dashboard: - sourceStats = collectStatsV2beta1(source.Spec) - } - - // Execute the conversion - err := conversionFunc(a, b, scope) - if err != nil { - return err - } - - // Collect target statistics - var targetStats dashboardStats - switch target := b.(type) { - case *dashv0.Dashboard: - if target.Spec.Object != nil { - targetStats = collectStatsV0V1(target.Spec.Object) - } - case *dashv1.Dashboard: - if target.Spec.Object != nil { - targetStats = collectStatsV0V1(target.Spec.Object) - } - case *dashv2alpha1.Dashboard: - targetStats = collectStatsV2alpha1(target.Spec) - case *dashv2beta1.Dashboard: - targetStats = collectStatsV2beta1(target.Spec) - } - - // Detect if data was lost - if dataLossErr := detectConversionDataLoss(sourceStats, targetStats, sourceFuncName, targetFuncName); dataLossErr != nil { - logger.Error("Dashboard conversion data loss detected", - "sourceFunc", sourceFuncName, - "targetFunc", targetFuncName, - "sourcePanels", sourceStats.panelCount, - "targetPanels", targetStats.panelCount, - "sourceQueries", sourceStats.queryCount, - "targetQueries", targetStats.queryCount, - "sourceAnnotations", sourceStats.annotationCount, - "targetAnnotations", targetStats.annotationCount, - "sourceLinks", sourceStats.linkCount, - "targetLinks", targetStats.linkCount, - "error", dataLossErr, - ) - return dataLossErr - } - - logger.Debug("Dashboard conversion completed without data loss", - "sourceFunc", sourceFuncName, - "targetFunc", targetFuncName, - "panels", targetStats.panelCount, - "queries", targetStats.queryCount, - "annotations", targetStats.annotationCount, - "links", targetStats.linkCount, - ) - - return nil - } -} diff --git a/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection_test.go b/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection_test.go index 816fd871029..5c28c98b861 100644 --- a/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection_test.go +++ b/apps/dashboard/pkg/migration/conversion/conversion_data_loss_detection_test.go @@ -410,158 +410,167 @@ func TestCountPanelsV2(t *testing.T) { } func TestDetectConversionDataLoss(t *testing.T) { - tests := []struct { - name string - sourceStats dashboardStats - targetStats dashboardStats - expectError bool - errorMsg string - }{ - { - name: "perfect match - no data loss", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - variableCount: 2, + // Tests that expect NO error (no data loss) + t.Run("success cases", func(t *testing.T) { + successTests := []struct { + name string + sourceStats dashboardStats + targetStats dashboardStats + }{ + { + name: "perfect match - no data loss", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + variableCount: 2, + }, + targetStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + variableCount: 2, + }, }, - targetStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - variableCount: 2, + { + name: "panel count increased (allowed)", + sourceStats: dashboardStats{ + panelCount: 2, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, + targetStats: dashboardStats{ + panelCount: 3, // Added a panel (OK) + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, }, - expectError: false, - }, - { - name: "panel count decreased (data loss)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, + { + name: "annotation count increased (allowed - default annotations)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 0, + linkCount: 1, + }, + targetStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 1, // Added default annotation (OK) + linkCount: 1, + }, }, - targetStats: dashboardStats{ - panelCount: 2, // Lost a panel! - queryCount: 5, - annotationCount: 2, - linkCount: 1, - }, - expectError: true, - errorMsg: "panel count decreased", - }, - { - name: "panel count increased (allowed)", - sourceStats: dashboardStats{ - panelCount: 2, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - }, - targetStats: dashboardStats{ - panelCount: 3, // Added a panel (OK) - queryCount: 5, - annotationCount: 2, - linkCount: 1, - }, - expectError: false, - }, - { - name: "query count decreased (data loss)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - }, - targetStats: dashboardStats{ - panelCount: 3, - queryCount: 3, // Lost queries! - annotationCount: 2, - linkCount: 1, - }, - expectError: true, - errorMsg: "query count decreased", - }, - { - name: "annotation count decreased (data loss)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - }, - targetStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 1, // Lost annotation! - linkCount: 1, - }, - expectError: true, - errorMsg: "annotation count decreased", - }, - { - name: "annotation count increased (allowed - default annotations)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 0, - linkCount: 1, - }, - targetStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 1, // Added default annotation (OK) - linkCount: 1, - }, - expectError: false, - }, - { - name: "variable count decreased (data loss)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - variableCount: 3, - }, - targetStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - variableCount: 1, // Lost variables! - }, - expectError: true, - errorMsg: "variable count decreased", - }, - { - name: "multiple decreases (data loss)", - sourceStats: dashboardStats{ - panelCount: 3, - queryCount: 5, - annotationCount: 2, - linkCount: 1, - variableCount: 2, - }, - targetStats: dashboardStats{ - panelCount: 2, // Lost panel - queryCount: 3, // Lost queries - annotationCount: 2, - linkCount: 0, // Lost link - variableCount: 2, - }, - expectError: true, - errorMsg: "panel count decreased", - }, - } + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := detectConversionDataLoss(tt.sourceStats, tt.targetStats, "TestSource", "TestTarget") - if tt.expectError { + for _, tt := range successTests { + t.Run(tt.name, func(t *testing.T) { + err := detectConversionDataLoss(tt.sourceStats, tt.targetStats, "TestSource", "TestTarget") + assert.NoError(t, err) + }) + } + }) + + // Tests that expect an error (data loss detected) + t.Run("data loss cases", func(t *testing.T) { + errorTests := []struct { + name string + sourceStats dashboardStats + targetStats dashboardStats + errorMsg string + }{ + { + name: "panel count decreased (data loss)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, + targetStats: dashboardStats{ + panelCount: 2, // Lost a panel! + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, + errorMsg: "panel count decreased", + }, + { + name: "query count decreased (data loss)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, + targetStats: dashboardStats{ + panelCount: 3, + queryCount: 3, // Lost queries! + annotationCount: 2, + linkCount: 1, + }, + errorMsg: "query count decreased", + }, + { + name: "annotation count decreased (data loss)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + }, + targetStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 1, // Lost annotation! + linkCount: 1, + }, + errorMsg: "annotation count decreased", + }, + { + name: "variable count decreased (data loss)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + variableCount: 3, + }, + targetStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + variableCount: 1, // Lost variables! + }, + errorMsg: "variable count decreased", + }, + { + name: "multiple decreases (data loss)", + sourceStats: dashboardStats{ + panelCount: 3, + queryCount: 5, + annotationCount: 2, + linkCount: 1, + variableCount: 2, + }, + targetStats: dashboardStats{ + panelCount: 2, // Lost panel + queryCount: 3, // Lost queries + annotationCount: 2, + linkCount: 0, // Lost link + variableCount: 2, + }, + errorMsg: "panel count decreased", + }, + } + + for _, tt := range errorTests { + t.Run(tt.name, func(t *testing.T) { + err := detectConversionDataLoss(tt.sourceStats, tt.targetStats, "TestSource", "TestTarget") require.Error(t, err) assert.Contains(t, err.Error(), tt.errorMsg) @@ -569,11 +578,9 @@ func TestDetectConversionDataLoss(t *testing.T) { var detectConversionDataLossErr *ConversionDataLossError require.ErrorAs(t, err, &detectConversionDataLossErr) assert.Equal(t, "TestSource_to_TestTarget", detectConversionDataLossErr.GetFunctionName()) - } else { - assert.NoError(t, err) - } - }) - } + }) + } + }) } func TestCollectStatsV0V1(t *testing.T) { @@ -687,140 +694,692 @@ func TestConversionDataLossError(t *testing.T) { assert.Equal(t, "data loss detected in TestFunc (v0alpha1 → v1beta1): test error message", err.Error()) } -// Integration test showing how validation works with actual dashboard types -func TestWithConversionValidation_Integration(t *testing.T) { - // Create a source dashboard (v0) with specific counts - sourceV0 := &dashv0.Dashboard{ - Spec: dashv0.DashboardSpec{ - Object: map[string]interface{}{ - "panels": []interface{}{ - map[string]interface{}{ - "id": float64(1), - "type": "graph", - "targets": []interface{}{ - map[string]interface{}{"refId": "A"}, - }, - }, - map[string]interface{}{ - "id": float64(2), - "type": "table", - "targets": []interface{}{ - map[string]interface{}{"refId": "A"}, - map[string]interface{}{"refId": "B"}, - }, - }, - }, - "annotations": map[string]interface{}{ - "list": []interface{}{ - map[string]interface{}{"name": "Annotation 1"}, - }, - }, - "links": []interface{}{ - map[string]interface{}{"title": "Link 1"}, - }, - }, - }, - } +// TestWithConversionMetrics_DataLossOnlyCheckedOnSuccess verifies that the +// withConversionMetrics wrapper only runs data loss detection when conversion succeeds. +func TestWithConversionMetrics_DataLossOnlyCheckedOnSuccess(t *testing.T) { + conversionError := errors.New("conversion failed") - // Create a target dashboard (v1) with matching counts - targetV1 := &dashv1.Dashboard{ - Spec: dashv1.DashboardSpec{ - Object: map[string]interface{}{ - "panels": []interface{}{ - map[string]interface{}{ - "id": float64(1), - "type": "graph", - "targets": []interface{}{ - map[string]interface{}{"refId": "A"}, - }, - }, - map[string]interface{}{ - "id": float64(2), - "type": "table", - "targets": []interface{}{ - map[string]interface{}{"refId": "A"}, - map[string]interface{}{"refId": "B"}, - }, - }, - }, - "annotations": map[string]interface{}{ - "list": []interface{}{ - map[string]interface{}{"name": "Annotation 1"}, - }, - }, - "links": []interface{}{ - map[string]interface{}{"title": "Link 1"}, - }, - }, - }, - } + // Test 1: Conversion fails - data loss should NOT be checked + t.Run("data loss not checked when conversion fails", func(t *testing.T) { + mockFailingConversion := func(a, b interface{}, scope conversion.Scope) error { + return conversionError + } - // Mock conversion function that just copies the spec - mockConversion := func(a, b interface{}, scope conversion.Scope) error { - source := a.(*dashv0.Dashboard) - target := b.(*dashv1.Dashboard) - target.Spec = dashv1.DashboardSpec{Object: source.Spec.Object} - return nil - } + wrappedFunc := withConversionMetrics(dashv0.APIVERSION, dashv1.APIVERSION, mockFailingConversion) - // Wrap with validation - validatedFunc := withConversionDataLossDetection("V0", "V1beta1", mockConversion) - - // This should pass validation - err := validatedFunc(sourceV0, targetV1, nil) - assert.NoError(t, err) -} - -func TestWithConversionValidation_DataLoss(t *testing.T) { - // Create a source dashboard (v0) with 2 panels - sourceV0 := &dashv0.Dashboard{ - Spec: dashv0.DashboardSpec{ - Object: map[string]interface{}{ - "panels": []interface{}{ - map[string]interface{}{ - "id": float64(1), - "type": "graph", + sourceV0 := &dashv0.Dashboard{ + Spec: dashv0.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{"id": float64(1), "type": "graph"}, + map[string]interface{}{"id": float64(2), "type": "table"}, }, - map[string]interface{}{ - "id": float64(2), - "type": "table", - }, - }, - }, - }, - } - - targetV1 := &dashv1.Dashboard{} - - // Mock conversion function that loses a panel - mockBadConversion := func(a, b interface{}, scope conversion.Scope) error { - target := b.(*dashv1.Dashboard) - // Only copy 1 panel instead of 2 - target.Spec = dashv1.DashboardSpec{ - Object: map[string]interface{}{ - "panels": []interface{}{ - map[string]interface{}{ - "id": float64(1), - "type": "graph", - }, - // Missing panel 2! }, }, } - return nil + targetV1 := &dashv1.Dashboard{} // Empty - would trigger data loss if checked + + // Execute - withConversionMetrics always returns nil to avoid 500s + err := wrappedFunc(sourceV0, targetV1, nil) + require.NoError(t, err, "withConversionMetrics should return nil even on conversion failure") + }) + + // Test 2: Conversion succeeds but data loss occurs - should detect data loss + t.Run("data loss detected when conversion succeeds but loses data", func(t *testing.T) { + mockDataLosingConversion := func(a, b interface{}, scope conversion.Scope) error { + // Conversion "succeeds" but loses a panel + target := b.(*dashv1.Dashboard) + target.Spec = dashv1.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + // Only 1 panel instead of 2 + map[string]interface{}{"id": float64(1), "type": "graph"}, + }, + }, + } + return nil + } + + wrappedFunc := withConversionMetrics(dashv0.APIVERSION, dashv1.APIVERSION, mockDataLosingConversion) + + sourceV0 := &dashv0.Dashboard{ + Spec: dashv0.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{"id": float64(1), "type": "graph"}, + map[string]interface{}{"id": float64(2), "type": "table"}, + }, + }, + }, + } + targetV1 := &dashv1.Dashboard{} + + // Execute - withConversionMetrics always returns nil + err := wrappedFunc(sourceV0, targetV1, nil) + require.NoError(t, err, "withConversionMetrics should return nil even on data loss") + + // The data loss is logged and recorded in metrics, but no error is returned to API server + }) +} + +// TestDataLossDetectionReturnsCorrectError verifies that when data loss is detected, +// a ConversionDataLossError is returned with proper function name and details. +func TestDataLossDetectionReturnsCorrectError(t *testing.T) { + t.Run("panel loss returns ConversionDataLossError", func(t *testing.T) { + sourceV0 := &dashv0.Dashboard{ + Spec: dashv0.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{"id": float64(1), "type": "graph"}, + map[string]interface{}{"id": float64(2), "type": "table"}, + }, + }, + }, + } + + targetV1 := &dashv1.Dashboard{ + Spec: dashv1.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + // Missing panel 2 + map[string]interface{}{"id": float64(1), "type": "graph"}, + }, + }, + }, + } + + err := checkConversionDataLoss(dashv0.APIVERSION, dashv1.APIVERSION, sourceV0, targetV1) + + require.Error(t, err) + var dataLossErr *ConversionDataLossError + require.ErrorAs(t, err, &dataLossErr) + assert.Equal(t, "V0_to_V1", dataLossErr.GetFunctionName()) + assert.Contains(t, err.Error(), "panel count decreased") + }) + + t.Run("query loss returns ConversionDataLossError", func(t *testing.T) { + sourceV0 := &dashv0.Dashboard{ + Spec: dashv0.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + map[string]interface{}{"refId": "B"}, + }, + }, + }, + }, + }, + } + + targetV1 := &dashv1.Dashboard{ + Spec: dashv1.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + // Missing query B + map[string]interface{}{"refId": "A"}, + }, + }, + }, + }, + }, + } + + err := checkConversionDataLoss(dashv0.APIVERSION, dashv1.APIVERSION, sourceV0, targetV1) + + require.Error(t, err) + var dataLossErr *ConversionDataLossError + require.ErrorAs(t, err, &dataLossErr) + assert.Contains(t, err.Error(), "query count decreased") + }) + + t.Run("no data loss returns nil", func(t *testing.T) { + sourceV0 := &dashv0.Dashboard{ + Spec: dashv0.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + }, + }, + }, + } + + targetV1 := &dashv1.Dashboard{ + Spec: dashv1.DashboardSpec{ + Object: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + }, + }, + }, + } + + err := checkConversionDataLoss(dashv0.APIVERSION, dashv1.APIVERSION, sourceV0, targetV1) + + require.NoError(t, err) + }) +} + +// TestDataLossDetection_AllTypesAllVersions verifies that data loss is detected for +// all types of loss (panels, queries, annotations, links, variables) across all +// version permutations (v0alpha1, v1beta1, v2alpha1, v2beta1). +func TestDataLossDetection_AllTypesAllVersions(t *testing.T) { + // Helper to create v0/v1 unstructured spec with data + createV0V1SpecWithData := func() map[string]interface{} { + return map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + map[string]interface{}{"refId": "B"}, + }, + }, + map[string]interface{}{ + "id": float64(2), + "type": "table", + "targets": []interface{}{ + map[string]interface{}{"refId": "C"}, + }, + }, + }, + "annotations": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "Annotation 1"}, + map[string]interface{}{"name": "Annotation 2"}, + }, + }, + "links": []interface{}{ + map[string]interface{}{"title": "Link 1"}, + map[string]interface{}{"title": "Link 2"}, + }, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "var1", "type": "query"}, + map[string]interface{}{"name": "var2", "type": "custom"}, + }, + }, + } } - // Wrap with data loss detection - validatedFunc := withConversionDataLossDetection("V0", "V1beta1", mockBadConversion) + // Helper to create v0/v1 unstructured spec with data loss (missing items) + createV0V1SpecWithLoss := func(lossType string) map[string]interface{} { + spec := createV0V1SpecWithData() + switch lossType { + case "panel": + spec["panels"] = []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + map[string]interface{}{"refId": "B"}, + }, + }, + // Missing panel 2 + } + case "query": + spec["panels"] = []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + // Missing query B + }, + }, + map[string]interface{}{ + "id": float64(2), + "type": "table", + "targets": []interface{}{ + map[string]interface{}{"refId": "C"}, + }, + }, + } + case "annotation": + spec["annotations"] = map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "Annotation 1"}, + // Missing annotation 2 + }, + } + case "link": + spec["links"] = []interface{}{ + map[string]interface{}{"title": "Link 1"}, + // Missing link 2 + } + case "variable": + spec["templating"] = map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "var1", "type": "query"}, + // Missing var2 + }, + } + } + return spec + } - // This should fail due to data loss - err := validatedFunc(sourceV0, targetV1, nil) - require.Error(t, err) - assert.Contains(t, err.Error(), "panel count decreased") + // Helper to create v2alpha1 dashboard with data + createV2alpha1WithData := func() *dashv2alpha1.Dashboard { + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: map[string]dashv2alpha1.DashboardElement{ + "panel1": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 1, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "B"}}, + }, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 2, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "C"}}, + }, + }, + }, + }, + }, + }, + }, + Annotations: []dashv2alpha1.DashboardAnnotationQueryKind{{}, {}}, + Links: []dashv2alpha1.DashboardDashboardLink{{}, {}}, + Variables: []dashv2alpha1.DashboardVariableKind{{}, {}}, + }, + } + } - var detectConversionDataLossErr *ConversionDataLossError - require.ErrorAs(t, err, &detectConversionDataLossErr) - assert.Equal(t, "V0_to_V1beta1", detectConversionDataLossErr.GetFunctionName()) + // Helper to create v2alpha1 dashboard with data loss + createV2alpha1WithLoss := func(lossType string) *dashv2alpha1.Dashboard { + dash := createV2alpha1WithData() + switch lossType { + case "panel": + delete(dash.Spec.Elements, "panel2") + case "query": + dash.Spec.Elements["panel1"].PanelKind.Spec.Data.Spec.Queries = []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + // Missing query B + } + case "annotation": + dash.Spec.Annotations = []dashv2alpha1.DashboardAnnotationQueryKind{{}} + case "link": + dash.Spec.Links = []dashv2alpha1.DashboardDashboardLink{{}} + case "variable": + dash.Spec.Variables = []dashv2alpha1.DashboardVariableKind{{}} + } + return dash + } + + // Helper to create v2beta1 dashboard with data + createV2beta1WithData := func() *dashv2beta1.Dashboard { + return &dashv2beta1.Dashboard{ + Spec: dashv2beta1.DashboardSpec{ + Elements: map[string]dashv2beta1.DashboardElement{ + "panel1": { + PanelKind: &dashv2beta1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2beta1.DashboardPanelSpec{ + Id: 1, + Data: dashv2beta1.DashboardQueryGroupKind{ + Spec: dashv2beta1.DashboardQueryGroupSpec{ + Queries: []dashv2beta1.DashboardPanelQueryKind{ + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "A"}}, + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "B"}}, + }, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2beta1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2beta1.DashboardPanelSpec{ + Id: 2, + Data: dashv2beta1.DashboardQueryGroupKind{ + Spec: dashv2beta1.DashboardQueryGroupSpec{ + Queries: []dashv2beta1.DashboardPanelQueryKind{ + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "C"}}, + }, + }, + }, + }, + }, + }, + }, + Annotations: []dashv2beta1.DashboardAnnotationQueryKind{{}, {}}, + Links: []dashv2beta1.DashboardDashboardLink{{}, {}}, + Variables: []dashv2beta1.DashboardVariableKind{{}, {}}, + }, + } + } + + // Helper to create v2beta1 dashboard with data loss + createV2beta1WithLoss := func(lossType string) *dashv2beta1.Dashboard { + dash := createV2beta1WithData() + switch lossType { + case "panel": + delete(dash.Spec.Elements, "panel2") + case "query": + dash.Spec.Elements["panel1"].PanelKind.Spec.Data.Spec.Queries = []dashv2beta1.DashboardPanelQueryKind{ + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "A"}}, + // Missing query B + } + case "annotation": + dash.Spec.Annotations = []dashv2beta1.DashboardAnnotationQueryKind{{}} + case "link": + dash.Spec.Links = []dashv2beta1.DashboardDashboardLink{{}} + case "variable": + dash.Spec.Variables = []dashv2beta1.DashboardVariableKind{{}} + } + return dash + } + + // All loss types to test + lossTypes := []struct { + name string + expectedErrMsg string + }{ + {"panel", "panel count decreased"}, + {"query", "query count decreased"}, + {"annotation", "annotation count decreased"}, + {"link", "link count decreased"}, + {"variable", "variable count decreased"}, + } + + // All version permutations (12 total: 4 versions × 3 targets each) + versionPairs := []struct { + sourceVersion string + targetVersion string + sourceAPI string + targetAPI string + }{ + // From v0alpha1 + {"v0alpha1", "v1beta1", dashv0.APIVERSION, dashv1.APIVERSION}, + {"v0alpha1", "v2alpha1", dashv0.APIVERSION, dashv2alpha1.APIVERSION}, + {"v0alpha1", "v2beta1", dashv0.APIVERSION, dashv2beta1.APIVERSION}, + // From v1beta1 + {"v1beta1", "v0alpha1", dashv1.APIVERSION, dashv0.APIVERSION}, + {"v1beta1", "v2alpha1", dashv1.APIVERSION, dashv2alpha1.APIVERSION}, + {"v1beta1", "v2beta1", dashv1.APIVERSION, dashv2beta1.APIVERSION}, + // From v2alpha1 + {"v2alpha1", "v0alpha1", dashv2alpha1.APIVERSION, dashv0.APIVERSION}, + {"v2alpha1", "v1beta1", dashv2alpha1.APIVERSION, dashv1.APIVERSION}, + {"v2alpha1", "v2beta1", dashv2alpha1.APIVERSION, dashv2beta1.APIVERSION}, + // From v2beta1 + {"v2beta1", "v0alpha1", dashv2beta1.APIVERSION, dashv0.APIVERSION}, + {"v2beta1", "v1beta1", dashv2beta1.APIVERSION, dashv1.APIVERSION}, + {"v2beta1", "v2alpha1", dashv2beta1.APIVERSION, dashv2alpha1.APIVERSION}, + } + + for _, vp := range versionPairs { + for _, lt := range lossTypes { + testName := fmt.Sprintf("%s_to_%s_%s_loss", vp.sourceVersion, vp.targetVersion, lt.name) + t.Run(testName, func(t *testing.T) { + var source, target interface{} + + // Create source dashboard with full data + switch vp.sourceVersion { + case "v0alpha1": + source = &dashv0.Dashboard{Spec: dashv0.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v1beta1": + source = &dashv1.Dashboard{Spec: dashv1.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v2alpha1": + source = createV2alpha1WithData() + case "v2beta1": + source = createV2beta1WithData() + } + + // Create target dashboard with data loss + switch vp.targetVersion { + case "v0alpha1": + target = &dashv0.Dashboard{Spec: dashv0.DashboardSpec{Object: createV0V1SpecWithLoss(lt.name)}} + case "v1beta1": + target = &dashv1.Dashboard{Spec: dashv1.DashboardSpec{Object: createV0V1SpecWithLoss(lt.name)}} + case "v2alpha1": + target = createV2alpha1WithLoss(lt.name) + case "v2beta1": + target = createV2beta1WithLoss(lt.name) + } + + // Check for data loss + err := checkConversionDataLoss(vp.sourceAPI, vp.targetAPI, source, target) + + // Verify data loss was detected + require.Error(t, err, "Expected %s loss to be detected for %s -> %s", lt.name, vp.sourceVersion, vp.targetVersion) + assert.Contains(t, err.Error(), lt.expectedErrMsg) + + // Verify it's a ConversionDataLossError + var dataLossErr *ConversionDataLossError + require.ErrorAs(t, err, &dataLossErr) + }) + } + } +} + +// TestDataLossDetection_NoFalsePositives_AllVersionPermutations verifies that no false positives +// are triggered for any attribute type when counts are equal or increased, across ALL version permutations. +func TestDataLossDetection_NoFalsePositives_AllVersionPermutations(t *testing.T) { + // Helper to create v0/v1 spec with data + createV0V1SpecWithData := func() map[string]interface{} { + return map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(1), + "type": "graph", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + map[string]interface{}{"refId": "B"}, + }, + }, + map[string]interface{}{ + "id": float64(2), + "type": "table", + "targets": []interface{}{ + map[string]interface{}{"refId": "C"}, + }, + }, + }, + "annotations": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "Annotation 1"}, + map[string]interface{}{"name": "Annotation 2"}, + }, + }, + "links": []interface{}{ + map[string]interface{}{"title": "Link 1"}, + map[string]interface{}{"title": "Link 2"}, + }, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{"name": "var1", "type": "query"}, + map[string]interface{}{"name": "var2", "type": "custom"}, + }, + }, + } + } + + // Helper to create v2alpha1 dashboard with data + createV2alpha1WithData := func() *dashv2alpha1.Dashboard { + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: map[string]dashv2alpha1.DashboardElement{ + "panel1": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 1, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "B"}}, + }, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 2, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "C"}}, + }, + }, + }, + }, + }, + }, + }, + Annotations: []dashv2alpha1.DashboardAnnotationQueryKind{{}, {}}, + Links: []dashv2alpha1.DashboardDashboardLink{{}, {}}, + Variables: []dashv2alpha1.DashboardVariableKind{{}, {}}, + }, + } + } + + // Helper to create v2beta1 dashboard with data + createV2beta1WithData := func() *dashv2beta1.Dashboard { + return &dashv2beta1.Dashboard{ + Spec: dashv2beta1.DashboardSpec{ + Elements: map[string]dashv2beta1.DashboardElement{ + "panel1": { + PanelKind: &dashv2beta1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2beta1.DashboardPanelSpec{ + Id: 1, + Data: dashv2beta1.DashboardQueryGroupKind{ + Spec: dashv2beta1.DashboardQueryGroupSpec{ + Queries: []dashv2beta1.DashboardPanelQueryKind{ + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "A"}}, + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "B"}}, + }, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2beta1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2beta1.DashboardPanelSpec{ + Id: 2, + Data: dashv2beta1.DashboardQueryGroupKind{ + Spec: dashv2beta1.DashboardQueryGroupSpec{ + Queries: []dashv2beta1.DashboardPanelQueryKind{ + {Spec: dashv2beta1.DashboardPanelQuerySpec{RefId: "C"}}, + }, + }, + }, + }, + }, + }, + }, + Annotations: []dashv2beta1.DashboardAnnotationQueryKind{{}, {}}, + Links: []dashv2beta1.DashboardDashboardLink{{}, {}}, + Variables: []dashv2beta1.DashboardVariableKind{{}, {}}, + }, + } + } + + // All 12 version permutations + versionPairs := []struct { + sourceVersion string + targetVersion string + sourceAPI string + targetAPI string + }{ + // From v0alpha1 + {"v0alpha1", "v1beta1", dashv0.APIVERSION, dashv1.APIVERSION}, + {"v0alpha1", "v2alpha1", dashv0.APIVERSION, dashv2alpha1.APIVERSION}, + {"v0alpha1", "v2beta1", dashv0.APIVERSION, dashv2beta1.APIVERSION}, + // From v1beta1 + {"v1beta1", "v0alpha1", dashv1.APIVERSION, dashv0.APIVERSION}, + {"v1beta1", "v2alpha1", dashv1.APIVERSION, dashv2alpha1.APIVERSION}, + {"v1beta1", "v2beta1", dashv1.APIVERSION, dashv2beta1.APIVERSION}, + // From v2alpha1 + {"v2alpha1", "v0alpha1", dashv2alpha1.APIVERSION, dashv0.APIVERSION}, + {"v2alpha1", "v1beta1", dashv2alpha1.APIVERSION, dashv1.APIVERSION}, + {"v2alpha1", "v2beta1", dashv2alpha1.APIVERSION, dashv2beta1.APIVERSION}, + // From v2beta1 + {"v2beta1", "v0alpha1", dashv2beta1.APIVERSION, dashv0.APIVERSION}, + {"v2beta1", "v1beta1", dashv2beta1.APIVERSION, dashv1.APIVERSION}, + {"v2beta1", "v2alpha1", dashv2beta1.APIVERSION, dashv2alpha1.APIVERSION}, + } + + for _, vp := range versionPairs { + testName := fmt.Sprintf("%s_to_%s_no_loss", vp.sourceVersion, vp.targetVersion) + t.Run(testName, func(t *testing.T) { + var source, target interface{} + + // Create source dashboard with full data + switch vp.sourceVersion { + case "v0alpha1": + source = &dashv0.Dashboard{Spec: dashv0.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v1beta1": + source = &dashv1.Dashboard{Spec: dashv1.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v2alpha1": + source = createV2alpha1WithData() + case "v2beta1": + source = createV2beta1WithData() + } + + // Create target dashboard with same data (no loss) + switch vp.targetVersion { + case "v0alpha1": + target = &dashv0.Dashboard{Spec: dashv0.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v1beta1": + target = &dashv1.Dashboard{Spec: dashv1.DashboardSpec{Object: createV0V1SpecWithData()}} + case "v2alpha1": + target = createV2alpha1WithData() + case "v2beta1": + target = createV2beta1WithData() + } + + // Check for data loss - should be none + err := checkConversionDataLoss(vp.sourceAPI, vp.targetAPI, source, target) + require.NoError(t, err, "Expected no data loss when data is preserved for %s -> %s", vp.sourceVersion, vp.targetVersion) + }) + } } // TestDataLossDetectionOnAllInputFiles tests all conversions from testdata/input @@ -1359,3 +1918,1148 @@ func extractQueryInfoV2beta1(spec dashv2beta1.DashboardSpec) []queryInfo { return queries } + +// TestDataLossDetection_IntegrationWithRealConversions performs actual conversions +// using the scheme and verifies that data loss detection works correctly. +func TestDataLossDetection_IntegrationWithRealConversions(t *testing.T) { + // Initialize the migrator with a test data source provider + dsProvider := testutil.NewDataSourceProvider(testutil.StandardTestConfig) + leProvider := testutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Set up conversion scheme + scheme := runtime.NewScheme() + err := RegisterConversions(scheme, dsProvider, leProvider) + require.NoError(t, err) + + // + // ========== V0/V1 -> V2 CONVERSIONS (These should all pass) ========== + // + + // Test 1: V0 Collapsed Rows -> V2alpha1 + t.Run("Integration_V0CollapsedRows_To_V2alpha1", func(t *testing.T) { + source := &dashv0.Dashboard{ + TypeMeta: metav1.TypeMeta{ + APIVersion: dashv0.APIVERSION, + Kind: "Dashboard", + }, + Spec: dashv0.DashboardSpec{ + Object: createV0V1WithCollapsedRows(2, 2, 1), + }, + } + sourceStats := collectStatsV0V1(source.Spec.Object) + + target := &dashv2alpha1.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV2alpha1(target.Spec) + + t.Logf("V0 Collapsed Rows -> V2alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 2: V0 Expanded Rows -> V2alpha1 + t.Run("Integration_V0ExpandedRows_To_V2alpha1", func(t *testing.T) { + source := &dashv0.Dashboard{ + TypeMeta: metav1.TypeMeta{ + APIVersion: dashv0.APIVERSION, + Kind: "Dashboard", + }, + Spec: dashv0.DashboardSpec{ + Object: createV0V1WithExpandedRows(2, 2, 1), + }, + } + sourceStats := collectStatsV0V1(source.Spec.Object) + + target := &dashv2alpha1.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV2alpha1(target.Spec) + + t.Logf("V0 Expanded Rows -> V2alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 3: V0 Flat Panels -> V2alpha1 + t.Run("Integration_V0FlatPanels_To_V2alpha1", func(t *testing.T) { + source := &dashv0.Dashboard{ + TypeMeta: metav1.TypeMeta{ + APIVersion: dashv0.APIVERSION, + Kind: "Dashboard", + }, + Spec: dashv0.DashboardSpec{ + Object: createV0V1FlatPanels(4, 1), + }, + } + sourceStats := collectStatsV0V1(source.Spec.Object) + + target := &dashv2alpha1.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV2alpha1(target.Spec) + + t.Logf("V0 Flat Panels -> V2alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 4: V0 Mixed Layout -> V2alpha1 + t.Run("Integration_V0MixedLayout_To_V2alpha1", func(t *testing.T) { + source := &dashv0.Dashboard{ + TypeMeta: metav1.TypeMeta{ + APIVersion: dashv0.APIVERSION, + Kind: "Dashboard", + }, + Spec: dashv0.DashboardSpec{ + Object: createV0V1MixedAllTypes(), + }, + } + sourceStats := collectStatsV0V1(source.Spec.Object) + + target := &dashv2alpha1.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV2alpha1(target.Spec) + + t.Logf("V0 Mixed Layout -> V2alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // + // ========== V2 -> V0/V1 CONVERSIONS ========== + // These tests document the current state of V2 -> V0 conversions. + // + + // Test 5: V2 Simple Tabs -> V0alpha1 + t.Run("Integration_V2SimpleTabs_To_V0alpha1", func(t *testing.T) { + source := createV2DashboardWithSimpleTabs() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Simple Tabs -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 6: V2 Rows Collapsed -> V0alpha1 + t.Run("Integration_V2RowsCollapsed_To_V0alpha1", func(t *testing.T) { + source := createV2DashboardWithRowsCollapsed() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Rows Collapsed -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 7: V2 Rows Expanded -> V0alpha1 + t.Run("Integration_V2RowsExpanded_To_V0alpha1", func(t *testing.T) { + source := createV2DashboardWithRowsExpanded() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Rows Expanded -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 8: V2 Tabs with Nested Rows -> V0alpha1 + t.Run("Integration_V2TabsWithNestedRows_To_V0alpha1", func(t *testing.T) { + source := createV2DashboardWithTabsAndNestedRows() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Tabs with Nested Rows -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 9: V2 Rows with Nested Tabs -> V0alpha1 + t.Run("Integration_V2RowsWithNestedTabs_To_V0alpha1", func(t *testing.T) { + source := createV2DashboardWithRowsAndNestedTabs() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Rows with Nested Tabs -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // Test 10: V2 Complex Nesting -> V0alpha1 + t.Run("Integration_V2ComplexNesting_To_V0alpha1", func(t *testing.T) { + source := createV2ComplexNestedDashboard() + source.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + sourceStats := collectStatsV2alpha1(source.Spec) + + target := &dashv0.Dashboard{} + err := scheme.Convert(source, target, nil) + require.NoError(t, err, "Conversion should succeed") + + targetStats := collectStatsV0V1(target.Spec.Object) + + t.Logf("V2 Complex Nesting -> V0alpha1:") + t.Logf(" Source: panels=%d, queries=%d", sourceStats.panelCount, sourceStats.queryCount) + t.Logf(" Target: panels=%d, queries=%d", targetStats.panelCount, targetStats.queryCount) + + assert.Equal(t, sourceStats.panelCount, targetStats.panelCount, "Panel count should match") + assert.Equal(t, sourceStats.queryCount, targetStats.queryCount, "Query count should match") + }) + + // + // ========== ROUND-TRIP CONVERSIONS ========== + // + + // Test 11: V2 -> V0 -> V2 round-trip (panel count should be preserved) + t.Run("Integration_RoundTrip_V2_V0_V2", func(t *testing.T) { + original := createV2DashboardWithSimpleTabs() + original.TypeMeta = metav1.TypeMeta{ + APIVersion: dashv2alpha1.APIVERSION, + Kind: "Dashboard", + } + originalStats := collectStatsV2alpha1(original.Spec) + + // V2 -> V0 + intermediate := &dashv0.Dashboard{} + err := scheme.Convert(original, intermediate, nil) + require.NoError(t, err, "V2 -> V0 conversion should succeed") + intermediateStats := collectStatsV0V1(intermediate.Spec.Object) + + // V0 -> V2 + final := &dashv2alpha1.Dashboard{} + err = scheme.Convert(intermediate, final, nil) + require.NoError(t, err, "V0 -> V2 conversion should succeed") + finalStats := collectStatsV2alpha1(final.Spec) + + t.Logf("Round-trip V2 -> V0 -> V2:") + t.Logf(" Original (V2): panels=%d, queries=%d", originalStats.panelCount, originalStats.queryCount) + t.Logf(" Intermediate (V0): panels=%d, queries=%d", intermediateStats.panelCount, intermediateStats.queryCount) + t.Logf(" Final (V2): panels=%d, queries=%d", finalStats.panelCount, finalStats.queryCount) + + // Panel count should be preserved through round-trip + assert.Equal(t, originalStats.panelCount, finalStats.panelCount, "Panel count should match after round-trip") + + // Document query behavior + if intermediateStats.queryCount < originalStats.queryCount { + t.Logf(" ⚠️ Queries lost in V2->V0: %d -> %d", originalStats.queryCount, intermediateStats.queryCount) + } + if finalStats.queryCount != originalStats.queryCount { + t.Logf(" ⚠️ Final query count differs: original=%d, final=%d", originalStats.queryCount, finalStats.queryCount) + } + }) + + // Test 12: V0 -> V2 -> V0 round-trip + t.Run("Integration_RoundTrip_V0_V2_V0", func(t *testing.T) { + original := &dashv0.Dashboard{ + TypeMeta: metav1.TypeMeta{ + APIVersion: dashv0.APIVERSION, + Kind: "Dashboard", + }, + Spec: dashv0.DashboardSpec{ + Object: createV0V1MixedAllTypes(), + }, + } + originalStats := collectStatsV0V1(original.Spec.Object) + + // V0 -> V2 + intermediate := &dashv2alpha1.Dashboard{} + err := scheme.Convert(original, intermediate, nil) + require.NoError(t, err, "V0 -> V2 conversion should succeed") + intermediateStats := collectStatsV2alpha1(intermediate.Spec) + + // V2 -> V0 + final := &dashv0.Dashboard{} + err = scheme.Convert(intermediate, final, nil) + require.NoError(t, err, "V2 -> V0 conversion should succeed") + finalStats := collectStatsV0V1(final.Spec.Object) + + t.Logf("Round-trip V0 -> V2 -> V0:") + t.Logf(" Original (V0): panels=%d, queries=%d", originalStats.panelCount, originalStats.queryCount) + t.Logf(" Intermediate (V2): panels=%d, queries=%d", intermediateStats.panelCount, intermediateStats.queryCount) + t.Logf(" Final (V0): panels=%d, queries=%d", finalStats.panelCount, finalStats.queryCount) + + // V0 -> V2 should preserve everything + assert.Equal(t, originalStats.panelCount, intermediateStats.panelCount, "V0->V2 should preserve panels") + assert.Equal(t, originalStats.queryCount, intermediateStats.queryCount, "V0->V2 should preserve queries") + + // Document what happens in V2 -> V0 + assert.Equal(t, originalStats.panelCount, finalStats.panelCount, "Panel count should match after round-trip") + + if finalStats.queryCount < originalStats.queryCount { + t.Logf(" ⚠️ Queries lost in V2->V0 leg: %d -> %d", intermediateStats.queryCount, finalStats.queryCount) + } + }) +} + +// Helper functions to create test dashboards + +// ========== V2 Dashboard Fixtures ========== + +// createV2DashboardWithSimpleTabs creates a V2 dashboard with simple TabsLayout +// Structure: TabsLayout > Tab1[panel1, panel2] + Tab2[panel3, panel4] +func createV2DashboardWithSimpleTabs() *dashv2alpha1.Dashboard { + elements := map[string]dashv2alpha1.DashboardElement{} + for i := 1; i <= 4; i++ { + tabNum := (i-1)/2 + 1 + elements[fmt.Sprintf("panel%d", i)] = dashv2alpha1.DashboardElement{ + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: float64(i), + Title: fmt.Sprintf("Panel %d - Tab %d", i, tabNum), + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + }, + }, + }, + }, + }, + } + } + + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + TabsLayoutKind: &dashv2alpha1.DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: dashv2alpha1.DashboardTabsLayoutSpec{ + Tabs: []dashv2alpha1.DashboardTabsLayoutTabKind{ + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 2"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +// createV2DashboardWithRowsCollapsed creates a V2 dashboard with RowsLayout (collapsed rows) +// Structure: RowsLayout > Row1(collapsed)[panel1, panel2] + Row2(collapsed)[panel3, panel4] +func createV2DashboardWithRowsCollapsed() *dashv2alpha1.Dashboard { + elements := map[string]dashv2alpha1.DashboardElement{} + for i := 1; i <= 4; i++ { + rowNum := (i-1)/2 + 1 + elements[fmt.Sprintf("panel%d", i)] = dashv2alpha1.DashboardElement{ + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: float64(i), + Title: fmt.Sprintf("Panel %d - Row %d (collapsed)", i, rowNum), + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + }, + }, + }, + }, + }, + } + } + + collapsed := true + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1 (collapsed)"), + Collapse: &collapsed, + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 2 (collapsed)"), + Collapse: &collapsed, + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +// createV2DashboardWithRowsExpanded creates a V2 dashboard with RowsLayout (expanded rows) +// Structure: RowsLayout > Row1(expanded)[panel1, panel2] + Row2(expanded)[panel3, panel4] +func createV2DashboardWithRowsExpanded() *dashv2alpha1.Dashboard { + elements := map[string]dashv2alpha1.DashboardElement{} + for i := 1; i <= 4; i++ { + rowNum := (i-1)/2 + 1 + elements[fmt.Sprintf("panel%d", i)] = dashv2alpha1.DashboardElement{ + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: float64(i), + Title: fmt.Sprintf("Panel %d - Row %d (expanded)", i, rowNum), + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{ + {Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}, + }, + }, + }, + }, + }, + } + } + + expanded := false // collapse=false means expanded + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1 (expanded)"), + Collapse: &expanded, + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 2 (expanded)"), + Collapse: &expanded, + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func createV0V1FlatPanels(numPanels, queriesPerPanel int) map[string]interface{} { + panels := make([]interface{}, numPanels) + for i := 0; i < numPanels; i++ { + targets := make([]interface{}, queriesPerPanel) + for q := 0; q < queriesPerPanel; q++ { + targets[q] = map[string]interface{}{"refId": fmt.Sprintf("%c", 'A'+q)} + } + panels[i] = map[string]interface{}{ + "id": float64(i + 1), + "title": fmt.Sprintf("Panel %d", i+1), + "type": "timeseries", + "targets": targets, + } + } + return map[string]interface{}{"panels": panels} +} + +func createV0V1WithCollapsedRows(numRows, panelsPerRow, queriesPerPanel int) map[string]interface{} { + panels := make([]interface{}, 0) + panelID := 1 + + for r := 0; r < numRows; r++ { + // Create collapsed panels for this row + collapsedPanels := make([]interface{}, panelsPerRow) + for p := 0; p < panelsPerRow; p++ { + targets := make([]interface{}, queriesPerPanel) + for q := 0; q < queriesPerPanel; q++ { + targets[q] = map[string]interface{}{"refId": fmt.Sprintf("%c", 'A'+q)} + } + collapsedPanels[p] = map[string]interface{}{ + "id": float64(panelID), + "title": fmt.Sprintf("Panel %d (in Row %d)", panelID, r+1), + "type": "timeseries", + "targets": targets, + } + panelID++ + } + + // Add row with collapsed panels + panels = append(panels, map[string]interface{}{ + "id": float64(100 + r), + "title": fmt.Sprintf("Row %d (collapsed)", r+1), + "type": "row", + "collapsed": true, + "panels": collapsedPanels, + }) + } + return map[string]interface{}{"panels": panels} +} + +func createV0V1WithExpandedRows(numRows, panelsPerRow, queriesPerPanel int) map[string]interface{} { + panels := make([]interface{}, 0) + panelID := 1 + + for r := 0; r < numRows; r++ { + // Add expanded row (no nested panels) + panels = append(panels, map[string]interface{}{ + "id": float64(100 + r), + "title": fmt.Sprintf("Row %d (expanded)", r+1), + "type": "row", + // No "panels" field when row is expanded + }) + + // Add panels after the row + for p := 0; p < panelsPerRow; p++ { + targets := make([]interface{}, queriesPerPanel) + for q := 0; q < queriesPerPanel; q++ { + targets[q] = map[string]interface{}{"refId": fmt.Sprintf("%c", 'A'+q)} + } + panels = append(panels, map[string]interface{}{ + "id": float64(panelID), + "title": fmt.Sprintf("Panel %d (after Row %d)", panelID, r+1), + "type": "timeseries", + "targets": targets, + }) + panelID++ + } + } + return map[string]interface{}{"panels": panels} +} + +func createV2DashboardWithTabsAndNestedRows() *dashv2alpha1.Dashboard { + // 2 tabs, each with 1 row containing 2 panels = 4 panels total + // Structure: TabsLayout > Tab1[RowsLayout > Row[panel1, panel2]] + Tab2[RowsLayout > Row[panel3, panel4]] + elements := map[string]dashv2alpha1.DashboardElement{ + "panel1": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 1, Title: "Panel 1 - Tab1/Row1", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 2, Title: "Panel 2 - Tab1/Row1", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "stat"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel3": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 3, Title: "Panel 3 - Tab2/Row1", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "gauge"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel4": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 4, Title: "Panel 4 - Tab2/Row1", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "table"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + } + + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + TabsLayoutKind: &dashv2alpha1.DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: dashv2alpha1.DashboardTabsLayoutSpec{ + Tabs: []dashv2alpha1.DashboardTabsLayoutTabKind{ + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 2"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func createV2DashboardWithRowsAndNestedTabs() *dashv2alpha1.Dashboard { + // 2 rows, each containing a tab layout with 2 panels = 4 panels total + // Structure: RowsLayout > Row1[TabsLayout > Tab[panel1, panel2]] + Row2[TabsLayout > Tab[panel3, panel4]] + elements := map[string]dashv2alpha1.DashboardElement{ + "panel1": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 1, Title: "Panel 1 - Row1/TabA", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel2": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 2, Title: "Panel 2 - Row1/TabB", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "stat"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel3": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 3, Title: "Panel 3 - Row2/TabA", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "gauge"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + "panel4": { + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: 4, Title: "Panel 4 - Row2/TabB", + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "table"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + }, + } + + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + TabsLayoutKind: &dashv2alpha1.DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: dashv2alpha1.DashboardTabsLayoutSpec{ + Tabs: []dashv2alpha1.DashboardTabsLayoutTabKind{ + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab A"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 2"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + TabsLayoutKind: &dashv2alpha1.DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: dashv2alpha1.DashboardTabsLayoutSpec{ + Tabs: []dashv2alpha1.DashboardTabsLayoutTabKind{ + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab A"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func createV2ComplexNestedDashboard() *dashv2alpha1.Dashboard { + // Complex: 2 tabs, Tab1 has 2 rows (each with 2 panels), Tab2 has 2 panels directly + // Total: 6 panels + // Structure: TabsLayout > Tab1[RowsLayout > Row1[p1,p2] + Row2[p3,p4]] + Tab2[GridLayout > p5,p6] + elements := map[string]dashv2alpha1.DashboardElement{} + for i := 1; i <= 6; i++ { + elements[fmt.Sprintf("panel%d", i)] = dashv2alpha1.DashboardElement{ + PanelKind: &dashv2alpha1.DashboardPanelKind{ + Kind: "Panel", + Spec: dashv2alpha1.DashboardPanelSpec{ + Id: float64(i), + Title: fmt.Sprintf("Panel %d", i), + VizConfig: dashv2alpha1.DashboardVizConfigKind{Kind: "timeseries"}, + Data: dashv2alpha1.DashboardQueryGroupKind{ + Spec: dashv2alpha1.DashboardQueryGroupSpec{ + Queries: []dashv2alpha1.DashboardPanelQueryKind{{Spec: dashv2alpha1.DashboardPanelQuerySpec{RefId: "A"}}}, + }, + }, + }, + }, + } + } + + return &dashv2alpha1.Dashboard{ + Spec: dashv2alpha1.DashboardSpec{ + Elements: elements, + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + TabsLayoutKind: &dashv2alpha1.DashboardTabsLayoutKind{ + Kind: "TabsLayout", + Spec: dashv2alpha1.DashboardTabsLayoutSpec{ + Tabs: []dashv2alpha1.DashboardTabsLayoutTabKind{ + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + RowsLayoutKind: &dashv2alpha1.DashboardRowsLayoutKind{ + Kind: "RowsLayout", + Spec: dashv2alpha1.DashboardRowsLayoutSpec{ + Rows: []dashv2alpha1.DashboardRowsLayoutRowKind{ + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 1"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel1"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel2"}}}, + }, + }, + }, + }, + }, + }, + { + Kind: "RowsLayoutRow", + Spec: dashv2alpha1.DashboardRowsLayoutRowSpec{ + Title: ptrString("Row 2"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel3"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel4"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + Kind: "TabsLayoutTab", + Spec: dashv2alpha1.DashboardTabsLayoutTabSpec{ + Title: ptrString("Tab 2"), + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{ + Items: []dashv2alpha1.DashboardGridLayoutItemKind{ + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel5"}}}, + {Spec: dashv2alpha1.DashboardGridLayoutItemSpec{Element: dashv2alpha1.DashboardElementReference{Name: "panel6"}}}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +// createV0V1MixedAllTypes creates a V0/V1 dashboard with ALL layout types: +// - Panels out of rows (flat, top-level) +// - Collapsed row with nested panels +// - Expanded row followed by panels +// Structure: [p1, p2, Row1(collapsed){p3,p4}, Row2(expanded), p5, p6] +// Total: 6 panels +func createV0V1MixedAllTypes() map[string]interface{} { + panels := []interface{}{ + // Flat panels out of rows (at the beginning) + map[string]interface{}{ + "id": float64(1), + "title": "Panel 1 (flat, no row)", + "type": "timeseries", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + map[string]interface{}{ + "id": float64(2), + "title": "Panel 2 (flat, no row)", + "type": "stat", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + + // Collapsed row with nested panels + map[string]interface{}{ + "id": float64(100), + "title": "Row 1 (collapsed)", + "type": "row", + "collapsed": true, + "panels": []interface{}{ + map[string]interface{}{ + "id": float64(3), + "title": "Panel 3 (in collapsed row)", + "type": "gauge", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + map[string]interface{}{ + "id": float64(4), + "title": "Panel 4 (in collapsed row)", + "type": "table", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + }, + }, + + // Expanded row (empty, panels follow after) + map[string]interface{}{ + "id": float64(101), + "title": "Row 2 (expanded)", + "type": "row", + // No "panels" field when row is expanded + }, + + // Panels after expanded row + map[string]interface{}{ + "id": float64(5), + "title": "Panel 5 (after expanded row)", + "type": "bargauge", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + map[string]interface{}{ + "id": float64(6), + "title": "Panel 6 (after expanded row)", + "type": "piechart", + "targets": []interface{}{ + map[string]interface{}{"refId": "A"}, + }, + }, + } + + return map[string]interface{}{"panels": panels} +} + +// ptrString is a helper to create a pointer to a string +func ptrString(s string) *string { + return &s +} diff --git a/apps/dashboard/pkg/migration/conversion/metrics.go b/apps/dashboard/pkg/migration/conversion/metrics.go index 5b2700bce60..5a60aa848de 100644 --- a/apps/dashboard/pkg/migration/conversion/metrics.go +++ b/apps/dashboard/pkg/migration/conversion/metrics.go @@ -17,7 +17,9 @@ import ( "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" ) -var logger = logging.DefaultLogger.With("logger", "dashboard.conversion") +func getLogger() logging.Logger { + return logging.DefaultLogger.With("logger", "dashboard.conversion") +} // getErroredSchemaVersionFunc determines the schema version function that errored func getErroredSchemaVersionFunc(err error) string { @@ -197,9 +199,9 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion ) if errorType == "schema_minimum_version_error" { - logger.Warn("Dashboard conversion failed", logFields...) + getLogger().Warn("Dashboard conversion failed", logFields...) } else { - logger.Error("Dashboard conversion failed", logFields...) + getLogger().Error("Dashboard conversion failed", logFields...) } } else { // Record success metrics @@ -235,7 +237,7 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion ) } - logger.Debug("Dashboard conversion succeeded", successLogFields...) + getLogger().Debug("Dashboard conversion succeeded", successLogFields...) } return nil diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json index 863d7b2a102..7c7c479199d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json @@ -76,9 +76,9 @@ "barGlow": false, "centerGlow": false, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -155,9 +155,9 @@ "barGlow": false, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -234,9 +234,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -313,9 +313,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -392,9 +392,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -471,9 +471,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -550,9 +550,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -642,9 +642,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -721,9 +721,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -800,9 +800,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -879,9 +879,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -975,9 +975,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1054,9 +1054,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1133,9 +1133,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1212,9 +1212,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1291,9 +1291,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1387,9 +1387,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1470,9 +1470,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1553,9 +1553,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1645,10 +1645,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1731,10 +1731,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1831,10 +1831,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1919,10 +1919,10 @@ "centerGlow": true, "rounded": true, "sparkline": false, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2005,10 +2005,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2091,10 +2091,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2147,4 +2147,4 @@ "title": "Panel tests - Gauge (new)", "uid": "panel-tests-gauge-new", "weekStart": "" -} \ No newline at end of file +} diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json index d4dd7980100..a3de6df336a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json @@ -956,9 +956,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1162,4 +1162,4 @@ "title": "Panel tests - Old gauge to new", "uid": "panel-tests-old-gauge-to-new", "weekStart": "" -} \ No newline at end of file +} diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2alpha1.json index 7a4a4d2c4b7..5fdde6e46be 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2alpha1.json @@ -237,5 +237,10 @@ "title": "V10 Table Thresholds Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2beta1.json index 69d75b09dcf..b2eb0336cf2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v10.table_thresholds.v42.v2beta1.json @@ -244,5 +244,10 @@ "title": "V10 Table Thresholds Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2alpha1.json index f427b5dbcb7..7185cc96f69 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2alpha1.json @@ -206,5 +206,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2beta1.json index 8e59bbd21ca..2a0200eb4cb 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v11.no-op-migration.v42.v2beta1.json @@ -213,5 +213,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2alpha1.json index f55769f82b2..5e69664e182 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2alpha1.json @@ -203,5 +203,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2beta1.json index febee713d97..e3d0bff265d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v12.template-variables.v42.v2beta1.json @@ -216,5 +216,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2alpha1.json index a1ef92d3d50..2d55803abbe 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2alpha1.json @@ -351,5 +351,10 @@ "title": "V13 Graph Thresholds Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2beta1.json index a41d04804b7..97fc1bfca44 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.graph_thresholds.v42.v2beta1.json @@ -362,5 +362,10 @@ "title": "V13 Graph Thresholds Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2alpha1.json index 3edc16edae6..818011bba5b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2alpha1.json @@ -129,5 +129,10 @@ "title": "Dashboard with minimal graph panel settings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2beta1.json index 8e18ac60c4d..3c12cff0619 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v13.minimal_graph_config.v42.v2beta1.json @@ -132,5 +132,10 @@ "title": "Dashboard with minimal graph panel settings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2alpha1.json index e4a62f00bc7..4c9617a521e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2alpha1.json @@ -210,5 +210,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2beta1.json index 975fc24c969..bf07de3de24 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v14.shared_crosshair_to_graph_tooltip.v42.v2beta1.json @@ -217,5 +217,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2alpha1.json index decdff12902..8d340c0f65e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2alpha1.json @@ -1004,5 +1004,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2beta1.json index 9db5193d5af..8070895766a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.mimir_rollout_debugging.v42.v2beta1.json @@ -1023,5 +1023,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2alpha1.json index 347556eb45d..cd1daaf4806 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2alpha1.json @@ -223,5 +223,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2beta1.json index 756f4a76074..63a51a69c96 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v15.no-op-migration.v42.v2beta1.json @@ -231,5 +231,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2alpha1.json index 1bf45848829..4c21c63a66a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2alpha1.json @@ -1455,5 +1455,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2beta1.json index d7f5fd82659..684b7b0f564 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.empty-rows-and-panels-array.v42.v2beta1.json @@ -1481,5 +1481,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2alpha1.json index 6cf8402f377..13e2bd96197 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2alpha1.json @@ -719,5 +719,10 @@ "title": "V16 Grid Layout Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2beta1.json index 2a6622f27a4..bbd59bf1a07 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.grid_layout_upgrade.v42.v2beta1.json @@ -739,5 +739,10 @@ "title": "V16 Grid Layout Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2alpha1.json index 221b01ffec5..52c19734101 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2alpha1.json @@ -1655,5 +1655,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2beta1.json index 7c05e2ab74c..49e7fb1d9e7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v16.span_zero_demo.v42.v2beta1.json @@ -1707,5 +1707,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2alpha1.json index aa9017f0720..05de2de2aa4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2alpha1.json @@ -636,5 +636,10 @@ "title": "V17 MinSpan to MaxPerRow Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2beta1.json index 697c0dadaac..778a653435a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v17.minspan_to_maxperrow.v42.v2beta1.json @@ -657,5 +657,10 @@ "title": "V17 MinSpan to MaxPerRow Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2alpha1.json index f154e2e2679..d524005a101 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2alpha1.json @@ -401,5 +401,10 @@ "title": "V18 Gauge Options Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2beta1.json index 6d1528a868a..b80c575053c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v18.gauge_options.v42.v2beta1.json @@ -412,5 +412,10 @@ "title": "V18 Gauge Options Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2alpha1.json index e51fc93636a..3d78d0167b3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2alpha1.json @@ -434,5 +434,10 @@ "title": "V19 Panel Links Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2beta1.json index 0dbf12ef898..2842123f75e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v19.panel_links.v42.v2beta1.json @@ -447,5 +447,10 @@ "title": "V19 Panel Links Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2alpha1.json index 129ba30c694..b6b4f83cdf7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2alpha1.json @@ -354,5 +354,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2beta1.json index 0c80ae34850..0b9d3bacd35 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v2.panels-and-services.v42.v2beta1.json @@ -365,5 +365,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2alpha1.json index 95d3f1d8834..1e077553a81 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2alpha1.json @@ -426,5 +426,10 @@ "title": "V20 Variable Syntax Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2beta1.json index f5c9e157c75..13327bcd9f6 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v20.variable_syntax_links.v42.v2beta1.json @@ -437,5 +437,10 @@ "title": "V20 Variable Syntax Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2alpha1.json index 0b2251f0dca..170e3c6ccad 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2alpha1.json @@ -401,5 +401,10 @@ "title": "V21 Data Links Series to Field Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2beta1.json index 8c9808adf32..b87e2c2fb9e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v21.data_links_series_to_field.v42.v2beta1.json @@ -412,5 +412,10 @@ "title": "V21 Data Links Series to Field Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2alpha1.json index dd03601b0ac..28eefba141b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2alpha1.json @@ -123,5 +123,10 @@ "title": "V22 Table Panel Styles Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2beta1.json index fd3dc0b587b..d9de4392f97 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v22.table_panel_align.v42.v2beta1.json @@ -126,5 +126,10 @@ "title": "V22 Table Panel Styles Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2alpha1.json index a40797444f4..3e93dbc7a66 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2alpha1.json @@ -374,5 +374,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2beta1.json index a97d9a51e2f..7286db67bf2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v23.multi_variable_alignment.v42.v2beta1.json @@ -391,5 +391,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2alpha1.json index c9dd5bffa51..206e53baee2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2alpha1.json @@ -1065,5 +1065,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2beta1.json index 3d74b493b8e..9eeeb11b247 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v24.table-angular.v42.v2beta1.json @@ -1101,5 +1101,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2alpha1.json index 3375a6bffe4..35c94ad87bc 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2alpha1.json @@ -217,5 +217,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2beta1.json index b873fdea3a3..1cdd7407a2d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v25.no-op-migration.v42.v2beta1.json @@ -226,5 +226,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2alpha1.json index a135614baba..d15f179ac31 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2alpha1.json @@ -240,5 +240,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2beta1.json index 378ae69f16a..59aad2fdb0b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v26.text2_to_text.v42.v2beta1.json @@ -247,5 +247,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2alpha1.json index 51cdcba85c1..1c1b3d0d39b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2alpha1.json @@ -207,5 +207,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2beta1.json index 3fdcd01a27c..b8596415db8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v27.repeated_panels_and_constant_variable.v42.v2beta1.json @@ -211,5 +211,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2alpha1.json index 54384f8dc06..75831381989 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2alpha1.json @@ -131,5 +131,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2beta1.json index 6626b919137..e79b7d1f7d7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.remove_variable_properties.v42.v2beta1.json @@ -134,5 +134,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2alpha1.json index 82fc46be585..78df8e5bf3a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2alpha1.json @@ -393,5 +393,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2beta1.json index 4eff500bef6..f9b06083b63 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_and_variable_properties.v42.v2beta1.json @@ -406,5 +406,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2alpha1.json index 0873ad2190c..7fa4e5ae944 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2alpha1.json @@ -622,5 +622,10 @@ "title": "V28 Singlestat and Variable Properties Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2beta1.json index 2b7d27024c6..5aaff274c67 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v28.singlestat_migration.v42.v2beta1.json @@ -642,5 +642,10 @@ "title": "V28 Singlestat and Variable Properties Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2alpha1.json index 2688367b33b..5a096f20011 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2alpha1.json @@ -435,5 +435,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2beta1.json index 8e57483ceb4..270c6912485 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v29.query_variables_refresh_and_options.v42.v2beta1.json @@ -458,5 +458,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2alpha1.json index db33eea5445..112dae69255 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2alpha1.json @@ -294,5 +294,10 @@ "title": "V3 No-Op Migration - but tests ensuring panel IDs are unique", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2beta1.json index 4a38725abc3..0a3a97926b8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v3.no-op.v42.v2beta1.json @@ -303,5 +303,10 @@ "title": "V3 No-Op Migration - but tests ensuring panel IDs are unique", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2alpha1.json index 60334f9ffb1..a045836b269 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2alpha1.json @@ -689,5 +689,10 @@ "title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2beta1.json index 6848d2d1714..5ae7d7d5ef4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v30.value_mappings_and_tooltip_options.v42.v2beta1.json @@ -705,5 +705,10 @@ "title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2alpha1.json index ac34174bfcb..6fe0fcc67c8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2alpha1.json @@ -669,5 +669,10 @@ "title": "V31 LabelsToFields Merge Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2beta1.json index d7f0684597c..5ca1fbff96c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v31.labels_to_fields_merge.v42.v2beta1.json @@ -684,5 +684,10 @@ "title": "V31 LabelsToFields Merge Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2alpha1.json index 3c58166f6c4..2042cc9aa49 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2alpha1.json @@ -310,5 +310,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2beta1.json index 1611cea2464..7a90cf0c2ac 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v32.no_op_migration.v42.v2beta1.json @@ -320,5 +320,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2alpha1.json index d41d2d7bda3..a7cccb454ae 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2alpha1.json @@ -720,5 +720,10 @@ "title": "V33 Panel Datasource Name to Ref Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2beta1.json index fbb8d83b728..55836cf469c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v33.panel_ds_name_to_ref.v42.v2beta1.json @@ -746,5 +746,10 @@ "title": "V33 Panel Datasource Name to Ref Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2alpha1.json index ad69702a739..e5308bb6102 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2alpha1.json @@ -1900,5 +1900,10 @@ "title": "CloudWatch Multiple Statistics Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2beta1.json index 79ee3db369e..329585edd02 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v34.multiple_stats_cloudwatch.v42.v2beta1.json @@ -1964,5 +1964,10 @@ "title": "CloudWatch Multiple Statistics Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2alpha1.json index c06dd85bb25..cbb59009f10 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2alpha1.json @@ -595,5 +595,10 @@ "title": "X-Axis Visibility Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2beta1.json index f9be2a6cee1..fde3ba9c006 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v35.ensure_x_axis_visibility.v42.v2beta1.json @@ -612,5 +612,10 @@ "title": "X-Axis Visibility Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2alpha1.json index 474b22d4ffb..97a0dd7d19c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2alpha1.json @@ -1029,5 +1029,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2beta1.json index 6465fae7ed0..24c49f553ae 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v36.ds_name_to_ref.v42.v2beta1.json @@ -1065,5 +1065,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2alpha1.json index eb254828d3e..62c7806854a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2alpha1.json @@ -629,5 +629,10 @@ "title": "V37 Legend Normalization Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2beta1.json index 2803cc68947..9e112233023 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v37.legend_normalization.v42.v2beta1.json @@ -642,5 +642,10 @@ "title": "V37 Legend Normalization Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2alpha1.json index 92f6ec2ef8e..dd8087721a9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2alpha1.json @@ -659,5 +659,10 @@ "title": "V38 Table Migration Comprehensive Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2beta1.json index 493b6147404..3a75ab8b887 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.table_displaymode_comprehensive.v42.v2beta1.json @@ -671,5 +671,10 @@ "title": "V38 Table Migration Comprehensive Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2alpha1.json index a1fb5ca81af..f7ed9a945bf 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2alpha1.json @@ -659,5 +659,10 @@ "title": "V38 Table Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2beta1.json index 81cc875df07..f8eb9ef3616 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v38.timeseries_table_display_mode.v42.v2beta1.json @@ -671,5 +671,10 @@ "title": "V38 Table Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2alpha1.json index 37eb51fbdff..368f059af62 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2alpha1.json @@ -586,5 +586,10 @@ "title": "V39 TimeSeriesTable Transformation Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2beta1.json index 3b56960e026..99de7609d9a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v39.transform_timeseries_table.v42.v2beta1.json @@ -596,5 +596,10 @@ "title": "V39 TimeSeriesTable Transformation Migration Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2alpha1.json index e6701e601c7..983b06ebf43 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2alpha1.json @@ -237,5 +237,10 @@ "title": "V4 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2beta1.json index b6d1aa7d200..4e523705cb0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v4.no-op.v42.v2beta1.json @@ -244,5 +244,10 @@ "title": "V4 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2alpha1.json index 10f6bbf20f4..40df255245e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Empty String Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2beta1.json index a6cabe62d43..44136818b17 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_empty_string.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Empty String Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2alpha1.json index 13a901e94af..e149f9f6a45 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Boolean False Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2beta1.json index 7b5f900ef36..0499fbc7328 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_false.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Boolean False Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2alpha1.json index 04b94fc3919..95b61ef4e22 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Refresh Not Set Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2beta1.json index 1f09949f995..93ce85b17e1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_not_set.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Refresh Not Set Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2alpha1.json index 103fe15fdf3..fd250ce03c6 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Numeric Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2beta1.json index 6904267e72b..f81e1b004e9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_numeric.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Numeric Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2alpha1.json index 9fe42b778dd..94fa361257d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "String Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2beta1.json index b1bd81269c1..f8818a9dbf3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_string.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "String Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2alpha1.json index 3524796f0aa..bd9f897d313 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Boolean Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2beta1.json index 76eea7dbbdc..eecd119a617 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v40.refresh_true.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Boolean Refresh Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2alpha1.json index 9036e54f22d..ed1ea8dc001 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "No Time Picker Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2beta1.json index 54e2cfeef43..6b3b49c11e5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.no_time_picker.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "No Time Picker Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2alpha1.json index 78e27ac4411..2806b7c6d8d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Time Picker No Time Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2beta1.json index 52de1909de4..5b9595d8f74 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_no_time_options.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Time Picker No Time Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2alpha1.json index 8115f687203..a48695c53ec 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2alpha1.json @@ -64,5 +64,10 @@ "title": "Time Picker Time Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2beta1.json index 9dffb1fc27e..3f31b897bf0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v41.time_picker_time_options.v42.v2beta1.json @@ -65,5 +65,10 @@ "title": "Time Picker Time Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2alpha1.json index ee97abbd7e2..6aa423b4431 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2alpha1.json @@ -123,5 +123,10 @@ "title": "v42 Migration Test - Harky Must", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2beta1.json index 3d267c3135b..79648901fc2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.harky_must.v42.v2beta1.json @@ -125,5 +125,10 @@ "title": "v42 Migration Test - Harky Must", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2alpha1.json index d231c6561ad..3474345415f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2alpha1.json @@ -461,5 +461,10 @@ "title": "v42 Migration Test - HideFrom Tooltip", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2beta1.json index a1e187676a5..6f0e6b08043 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v42.hidefrom_tooltip.v42.v2beta1.json @@ -468,5 +468,10 @@ "title": "v42 Migration Test - HideFrom Tooltip", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2alpha1.json index 3c5f977cf19..d39da494ad0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2alpha1.json @@ -237,5 +237,10 @@ "title": "V5 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2beta1.json index 10544c3c0fe..4795fc07dc5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v5.no-op.v42.v2beta1.json @@ -244,5 +244,10 @@ "title": "V5 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2alpha1.json index 5c98e01c0f0..60ef3b2ec90 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2alpha1.json @@ -332,5 +332,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2beta1.json index 19fbf9be88f..3396e17655d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v6.pulldowns_and_templating.v42.v2beta1.json @@ -344,5 +344,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2alpha1.json index 425516baea9..10b57eb4b69 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2alpha1.json @@ -142,5 +142,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2beta1.json index 72cb9aadb9b..252a510f233 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v7.timepicker.v42.v2beta1.json @@ -146,5 +146,10 @@ "title": "No Title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2alpha1.json index 0bf433b534a..7b4432f77b5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2alpha1.json @@ -247,5 +247,10 @@ "title": "V8 InfluxDB Query Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2beta1.json index d8ee1076605..40d7695e10c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v8.influxdb_query.v42.v2beta1.json @@ -252,5 +252,10 @@ "title": "V8 InfluxDB Query Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2alpha1.json index 0e4ea43800d..e39200fdc8e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2alpha1.json @@ -237,5 +237,10 @@ "title": "V9 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2beta1.json index 45487aaebb9..71ba1439de3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/migrated_dashboards_output/v1beta1-mig-v9.no-op.v42.v2beta1.json @@ -244,5 +244,10 @@ "title": "V9 No-Op Migration Test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2alpha1.json index 59ed9c457a1..c7f21881b4c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2alpha1.json @@ -558,5 +558,10 @@ "title": "Annotation filtering", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2beta1.json index 26e6d792f59..b7a613fc48b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.annotation-filtering.v42.v2beta1.json @@ -570,5 +570,10 @@ "title": "Annotation filtering", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json index 5449d222a2c..b6addcc81ed 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json @@ -1368,5 +1368,10 @@ "title": "Multi-lane annotations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json index e23c11c69ef..f806e27a98f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json @@ -1393,5 +1393,10 @@ "title": "Multi-lane annotations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2alpha1.json index 253b6471087..5fb32832089 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2alpha1.json @@ -7244,5 +7244,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2beta1.json index 54edeecb540..3ab6bb18228 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.v2beta1.json @@ -7345,5 +7345,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2alpha1.json index 5b384c10afc..f2cd5feadf1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2alpha1.json @@ -13081,5 +13081,10 @@ "title": "Datasource tests - Elasticsearch migration", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2beta1.json index 898dc2b2489..5ba04ac7b23 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.v2beta1.json @@ -13289,5 +13289,10 @@ "title": "Datasource tests - Elasticsearch migration", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2alpha1.json index 9eab3b48079..7cf034e5479 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2alpha1.json @@ -1450,5 +1450,10 @@ "title": "Datasource tests - Elasticsearch simple", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2beta1.json index 35e24952e8a..e358643fc42 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.v2beta1.json @@ -1472,5 +1472,10 @@ "title": "Datasource tests - Elasticsearch simple", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2alpha1.json index 462481754b0..0ed400e5163 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2alpha1.json @@ -239,5 +239,10 @@ "title": "Datasource tests - InfluxDB Logs", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2beta1.json index 7cfd839bf00..4bb33313829 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.v2beta1.json @@ -245,5 +245,10 @@ "title": "Datasource tests - InfluxDB Logs", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2alpha1.json index 3b776907f83..63037588772 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2alpha1.json @@ -317,5 +317,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2beta1.json index a6ebf10b8f0..19495974932 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.v2beta1.json @@ -327,5 +327,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2alpha1.json index d8a362c5029..f6e20264866 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2alpha1.json @@ -615,5 +615,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2beta1.json index 029c2bcf218..01d2f669fe1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.v2beta1.json @@ -627,5 +627,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2alpha1.json index e5078c03a19..c3feede3aca 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2alpha1.json @@ -1552,5 +1552,10 @@ "title": "Datasource tests - Loki query splitting", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2beta1.json index c2651fc75a2..6ac4f48fde4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v2beta1.json @@ -1577,5 +1577,10 @@ "title": "Datasource tests - Loki query splitting", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2alpha1.json index 3faa6bf6d7b..d7e3a8f61b9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2alpha1.json @@ -546,5 +546,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2beta1.json index b9acf971b1d..2d66d6ca855 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.v2beta1.json @@ -566,5 +566,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2alpha1.json index 915a7a7f790..69c1c822020 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2alpha1.json @@ -2280,5 +2280,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2beta1.json index f28e28bdded..5ddf1d701a7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.v2beta1.json @@ -2360,5 +2360,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2alpha1.json index bd3a41d6f40..6dc4619db1d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2alpha1.json @@ -548,5 +548,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2beta1.json index fbb9f11c316..ff02a8b9af0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.v2beta1.json @@ -568,5 +568,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2alpha1.json index 007f6f3f04c..631815cbc3f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2alpha1.json @@ -2123,5 +2123,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2beta1.json index 35767416230..6ab6536924f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.v2beta1.json @@ -2197,5 +2197,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2alpha1.json index 71e90cc1bac..7f3926d803e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2alpha1.json @@ -194,5 +194,10 @@ "title": "Datasource tests - OpenTSDB", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2beta1.json index 9566977e001..2959bf4ca8d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.v2beta1.json @@ -200,5 +200,10 @@ "title": "Datasource tests - OpenTSDB", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2alpha1.json index ccd7b7cac51..03a415d2b4d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2alpha1.json @@ -219,5 +219,10 @@ "title": "Datasource tests - OpenTSDB v2.3", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2beta1.json index dd46ba3407a..d3b15a72cf9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.v2beta1.json @@ -225,5 +225,10 @@ "title": "Datasource tests - OpenTSDB v2.3", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2alpha1.json index dd5972aef39..15b1ad08458 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2alpha1.json @@ -590,5 +590,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2beta1.json index 707c89428e3..d3572facb36 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.v2beta1.json @@ -610,5 +610,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2alpha1.json index 1b70b5d07eb..2ac583af244 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2alpha1.json @@ -2121,5 +2121,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2beta1.json index 7878e91dac5..9a9dea7f6f3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.v2beta1.json @@ -2195,5 +2195,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2alpha1.json index 58f0b2cf4df..d7dded5b99f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2alpha1.json @@ -1371,5 +1371,10 @@ "title": "Bar Gauge Demo Unfilled", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2beta1.json index b1046fe5ca3..f1d552852e8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.v2beta1.json @@ -1434,5 +1434,10 @@ "title": "Bar Gauge Demo Unfilled", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2alpha1.json index 9f0da1593eb..d31a3f25ea2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2alpha1.json @@ -1698,5 +1698,10 @@ "title": "TestData - Demo Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2beta1.json index 3e11006d1de..74943a89cf9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.v2beta1.json @@ -1745,5 +1745,10 @@ "title": "TestData - Demo Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2alpha1.json index ca657342364..e557b4f80a6 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2alpha1.json @@ -2880,5 +2880,10 @@ "title": "New Features in v7.4", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2beta1.json index 0c8b7143a91..0ad4e83973b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.v2beta1.json @@ -2955,5 +2955,10 @@ "title": "New Features in v7.4", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2alpha1.json index dcfd34332f4..ad6e731fd8d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2alpha1.json @@ -3951,5 +3951,10 @@ "title": "New Features in v8.0", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2beta1.json index 93b501b2222..4f087f02750 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.v2beta1.json @@ -4072,5 +4072,10 @@ "title": "New Features in v8.0", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2alpha1.json index 28ea4680f2e..160c0cdf458 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2alpha1.json @@ -673,5 +673,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2beta1.json index af64f4a2f02..8d1436def13 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.v2beta1.json @@ -687,5 +687,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2alpha1.json index 5f7f66dd3cc..1629c425da7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2alpha1.json @@ -261,5 +261,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2beta1.json index a4681683ba5..fd375b98fed 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.v2beta1.json @@ -263,5 +263,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2alpha1.json index f8f9922016b..dde9ab81f9c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2alpha1.json @@ -269,5 +269,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2beta1.json index 96c5247c4f4..04266fae41b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.v2beta1.json @@ -274,5 +274,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2alpha1.json index e7e365773f1..f1ef5ec1c11 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2alpha1.json @@ -326,5 +326,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2beta1.json index 8cb12e2e45f..8344ab2fd3e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.v2beta1.json @@ -331,5 +331,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2alpha1.json index 62c2ef1f9bc..b3c25fda752 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2alpha1.json @@ -326,5 +326,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2beta1.json index 4e7764ff205..facb775e9f1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.v2beta1.json @@ -331,5 +331,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2alpha1.json index cc3254f517b..f4a9ccc966a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2alpha1.json @@ -210,5 +210,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2beta1.json index df7016f1969..79c21a744ec 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.v2beta1.json @@ -212,5 +212,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2alpha1.json index 3d8e275556b..d3e309f9d05 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2alpha1.json @@ -469,5 +469,10 @@ "title": "Link Extensions (onClick)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2beta1.json index a783ef609f2..2d1d0f2e529 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.v2beta1.json @@ -482,5 +482,10 @@ "title": "Link Extensions (onClick)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2alpha1.json index 98e0b3fd047..fc48dd77960 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2alpha1.json @@ -327,5 +327,10 @@ "title": "Link Extensions (path)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2beta1.json index 5376f4e3832..8c998ee4b2c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.v2beta1.json @@ -333,5 +333,10 @@ "title": "Link Extensions (path)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2alpha1.json index 2f913de363f..1ed897f3cc8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2alpha1.json @@ -1167,5 +1167,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2beta1.json index 614ac4cabec..40cc11e8cb9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.v2beta1.json @@ -1201,5 +1201,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2alpha1.json index 6b40487f1d0..2b16e9ce76b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2alpha1.json @@ -168,5 +168,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2beta1.json index 0bd988b9902..59e7b0a96bc 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.v2beta1.json @@ -172,5 +172,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2alpha1.json index abab61bd552..7480afb75db 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2alpha1.json @@ -205,5 +205,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2beta1.json index 4fc9cedb62a..cd93214ebc3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.v2beta1.json @@ -209,5 +209,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2alpha1.json index 2d423c83c3d..f8f7fbc5350 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2alpha1.json @@ -565,5 +565,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2beta1.json index 2be4b39a06f..897abd7a0c7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.v2beta1.json @@ -574,5 +574,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2alpha1.json index 17ee9515ded..85cb38c13ff 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2alpha1.json @@ -485,5 +485,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2beta1.json index fbe90caa7bd..a1ec03cd26a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.v2beta1.json @@ -491,5 +491,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2alpha1.json index 05b4c6ed70c..123f410cac0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2alpha1.json @@ -136,5 +136,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2beta1.json index c9ba98c312b..0f3f2ab00a2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.v2beta1.json @@ -140,5 +140,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2alpha1.json index 16148c1ab23..36419c8e892 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2alpha1.json @@ -579,5 +579,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2beta1.json index 1f8d4d5eee6..2450d74b2f2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.v2beta1.json @@ -592,5 +592,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2alpha1.json index 171b505001e..710a4bc0280 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2alpha1.json @@ -334,5 +334,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2beta1.json index 4b2a7a98790..d93a4dca40f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.v2beta1.json @@ -349,5 +349,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json index 837c5cbfa5a..1fb39d454b9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json @@ -639,5 +639,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json index 2b61b379a04..ed73f35670a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json @@ -661,5 +661,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2alpha1.json index efa298e1ec2..9eaf955bb68 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2alpha1.json @@ -247,5 +247,10 @@ "title": "Test variable output", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2beta1.json index 17fa40a073b..395867814e0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.v2beta1.json @@ -255,5 +255,10 @@ "title": "Test variable output", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2alpha1.json index 8ae6fb9f2cd..3b34a0fd26e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2alpha1.json @@ -259,5 +259,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2beta1.json index b07bdc72f60..6379e2a3191 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.v2beta1.json @@ -265,5 +265,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2alpha1.json index 08f14e2614f..67579b40d8c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2alpha1.json @@ -198,5 +198,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2beta1.json index a1ff094b67b..d7c1db61b49 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.v2beta1.json @@ -207,5 +207,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2alpha1.json index f71a17e660d..83c4dd25a5c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2alpha1.json @@ -536,5 +536,10 @@ "title": "Live flakey stream (w/ liveNow)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2beta1.json index 892812351b8..89bea6fc4d4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.v2beta1.json @@ -545,5 +545,10 @@ "title": "Live flakey stream (w/ liveNow)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2alpha1.json index abf4e4cdcef..017171df8ef 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2alpha1.json @@ -536,5 +536,10 @@ "title": "Live flakey stream", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2beta1.json index 68c0083c4ab..32cec1120c6 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.v2beta1.json @@ -545,5 +545,10 @@ "title": "Live flakey stream", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2alpha1.json index f3777d0eedd..ca4649e18a8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2alpha1.json @@ -707,5 +707,10 @@ "title": "Live publish test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2beta1.json index 66e94e73744..e87b98713e2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.v2beta1.json @@ -720,5 +720,10 @@ "title": "Live publish test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2alpha1.json index efe66b835b3..ebe0f2098d8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2alpha1.json @@ -1034,5 +1034,10 @@ "title": "Live streaming examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2beta1.json index 5ad3b53ea98..051a06f0f94 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.v2beta1.json @@ -1054,5 +1054,10 @@ "title": "Live streaming examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2alpha1.json index f8826a3a699..2ad3f371914 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2alpha1.json @@ -1469,5 +1469,10 @@ "title": "Devenv - Panel migrations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2beta1.json index 8eea5775903..5e8d1db63c1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.v2beta1.json @@ -1520,5 +1520,10 @@ "title": "Devenv - Panel migrations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2alpha1.json index 78f2a2b6030..058a8806a5e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2alpha1.json @@ -859,5 +859,10 @@ "title": "BarChart - Panel Tests - Value sizing", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2beta1.json index c60ab8343a6..6c3fa8ae1a9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.v2beta1.json @@ -880,5 +880,10 @@ "title": "BarChart - Panel Tests - Value sizing", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2alpha1.json index 8f969cbef36..0f77a66f203 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2alpha1.json @@ -428,5 +428,10 @@ "title": "BarChart - Label Rotation \u0026 Skipping", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2beta1.json index 9290dd4ff14..3ad88c72861 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.v2beta1.json @@ -437,5 +437,10 @@ "title": "BarChart - Label Rotation \u0026 Skipping", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2alpha1.json index 522857ab19a..8bc7997b3d4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2alpha1.json @@ -535,5 +535,10 @@ "title": "BarChart - Panel Tests - Series toggle / bar widths", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2beta1.json index 9459814d20b..7a04ed00d31 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.v2beta1.json @@ -548,5 +548,10 @@ "title": "BarChart - Panel Tests - Series toggle / bar widths", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2alpha1.json index f25d5355ccc..e5369a162f7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2alpha1.json @@ -1682,5 +1682,10 @@ "title": "BarChart - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2beta1.json index 0d8c2dbf2a1..208c4c112c0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.v2beta1.json @@ -1721,5 +1721,10 @@ "title": "BarChart - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2alpha1.json index db478ad7e3d..bd95597bf13 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2alpha1.json @@ -1127,5 +1127,10 @@ "title": "Panel Tests - Bar Chart Tooltips \u0026 Legends", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2beta1.json index 4ea638cf0eb..3109835cd91 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.v2beta1.json @@ -1155,5 +1155,10 @@ "title": "Panel Tests - Bar Chart Tooltips \u0026 Legends", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2alpha1.json index 0e540c7e66e..290ee546192 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2alpha1.json @@ -1412,5 +1412,10 @@ "title": "Bar Gauge Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2beta1.json index cc190aaa2e0..ebfa6d31425 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.v2beta1.json @@ -1548,5 +1548,10 @@ "title": "Bar Gauge Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2alpha1.json index c88ce3b28e1..bd408ee62ee 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2alpha1.json @@ -1922,5 +1922,10 @@ "title": "Panel Tests - Bar Gauge", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2beta1.json index 1aa4b0e1a85..cc604bc2045 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.v2beta1.json @@ -2052,5 +2052,10 @@ "title": "Panel Tests - Bar Gauge", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2alpha1.json index 4f782695385..2aa2d935923 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2alpha1.json @@ -1214,5 +1214,10 @@ "title": "Panel Tests - Bar Gauge 2", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2beta1.json index 777a64366ba..92be6f0c11c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.v2beta1.json @@ -1262,5 +1262,10 @@ "title": "Panel Tests - Bar Gauge 2", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2alpha1.json index 97283f755fb..6d92fbd46bb 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2alpha1.json @@ -640,5 +640,10 @@ "title": "Candlestick", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2beta1.json index 83ab2dd5571..0ce4c776fb5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.v2beta1.json @@ -653,5 +653,10 @@ "title": "Candlestick", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2alpha1.json index f5cb9c3c909..2976d37dc0f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2alpha1.json @@ -4064,5 +4064,10 @@ "title": "Panel Tests - Canvas Connection Examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2beta1.json index 49910daa683..47fb71205e2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.v2beta1.json @@ -4074,5 +4074,10 @@ "title": "Panel Tests - Canvas Connection Examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2alpha1.json index e5648af2325..c9e5b0662df 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2alpha1.json @@ -3503,5 +3503,10 @@ "title": "Panel Tests - Canvas Datalinks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2beta1.json index 8f705b659b6..d63bb93d321 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.v2beta1.json @@ -3506,5 +3506,10 @@ "title": "Panel Tests - Canvas Datalinks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2alpha1.json index b32f594a99e..8d2bdc0d36c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2alpha1.json @@ -4096,5 +4096,10 @@ "title": "Panel Tests - Canvas Examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2beta1.json index aa7b95b6dca..0c650fd4f87 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.v2beta1.json @@ -4113,5 +4113,10 @@ "title": "Panel Tests - Canvas Examples", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2alpha1.json index 7d0174aecaa..1cb5a102169 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2alpha1.json @@ -871,5 +871,10 @@ "title": "Panel Tests - Auto Decimals", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2beta1.json index c2083797bdf..6def9ba580b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.v2beta1.json @@ -899,5 +899,10 @@ "title": "Panel Tests - Auto Decimals", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2alpha1.json index 12d4efa68b6..bcb9fba2fe9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2alpha1.json @@ -471,5 +471,10 @@ "title": "Gradient Color modes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2beta1.json index 3c1e8803dda..54a0b0d198d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.v2beta1.json @@ -481,5 +481,10 @@ "title": "Gradient Color modes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2alpha1.json index 379d5597285..e394514efd5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2alpha1.json @@ -2073,5 +2073,10 @@ "title": "Lazy Loading", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2beta1.json index 2767a76315e..eb82d064496 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.v2beta1.json @@ -2229,5 +2229,10 @@ "title": "Lazy Loading", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2alpha1.json index 03cea1fc2c8..ce9d8e17187 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2alpha1.json @@ -1843,5 +1843,10 @@ "title": "Panel \u0026 data links in stat, gauge and bargauge", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2beta1.json index 6fbdb415483..fe5fbfda4aa 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.v2beta1.json @@ -1881,5 +1881,10 @@ "title": "Panel \u0026 data links in stat, gauge and bargauge", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2alpha1.json index 5723bef32ca..d759c27c39b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2alpha1.json @@ -1107,5 +1107,10 @@ "title": "Panel Tests - With \u0026 Without title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2beta1.json index 93173c86f2b..1143716a2b3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.v2beta1.json @@ -1137,5 +1137,10 @@ "title": "Panel Tests - With \u0026 Without title", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2alpha1.json index 15f76709e5e..0c5ddf56280 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2alpha1.json @@ -614,5 +614,10 @@ "title": "Datasource tests - Shared Queries", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2beta1.json index be9740c4b92..15ca6962fc8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.v2beta1.json @@ -630,5 +630,10 @@ "title": "Datasource tests - Shared Queries", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2alpha1.json index d28a40a68f1..08343d13d55 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2alpha1.json @@ -139,5 +139,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2beta1.json index aedaf24d379..aaf9456f795 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.v2beta1.json @@ -141,5 +141,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2alpha1.json index 7b820200d04..1a01292a17e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2alpha1.json @@ -138,5 +138,10 @@ "title": "Datagrid example", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2beta1.json index ec12c171f3a..fc5049c6d79 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.v2beta1.json @@ -144,5 +144,10 @@ "title": "Datagrid example", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2alpha1.json index 2ca31f3255f..69033e3dfc5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2alpha1.json @@ -130,5 +130,10 @@ "title": "Panel Tests - Flame Graph", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2beta1.json index 10b0b3ed930..982f4088303 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.v2beta1.json @@ -134,5 +134,10 @@ "title": "Panel Tests - Flame Graph", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2alpha1.json index f9046bea886..706d0bafd47 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2alpha1.json @@ -499,5 +499,10 @@ "title": "Panel Tests - Gauge Multi Series", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2beta1.json index da3a0f5e6f1..d0cbc9aa44e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.v2beta1.json @@ -534,5 +534,10 @@ "title": "Panel Tests - Gauge Multi Series", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2alpha1.json index bd213346a6a..77874ee6291 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2alpha1.json @@ -1429,5 +1429,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2beta1.json index 4e6d53c09ab..663abfbf74c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.v2beta1.json @@ -1469,5 +1469,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v1beta1.json index 3084bbd32ea..2eeb3040e6d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v1beta1.json @@ -81,10 +81,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -160,10 +160,10 @@ "effects": { "barGlow": false, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -239,10 +239,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -318,10 +318,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -397,10 +397,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -476,10 +476,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -555,10 +555,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -647,10 +647,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -726,10 +726,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -805,10 +805,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -884,10 +884,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -980,10 +980,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1059,10 +1059,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1138,10 +1138,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1217,10 +1217,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1296,10 +1296,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1392,10 +1392,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1475,10 +1475,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1558,10 +1558,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1650,11 +1650,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1736,11 +1736,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1836,11 +1836,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1923,12 +1923,12 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "sparkline": false, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2010,11 +2010,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2096,11 +2096,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2alpha1.json index 5b5c3951ad8..4aecf4e0d9c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2alpha1.json @@ -77,10 +77,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -170,11 +170,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -267,11 +267,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -365,12 +365,12 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "sparkline": false, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -464,11 +464,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -561,11 +561,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -657,10 +657,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -749,10 +749,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -841,10 +841,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -933,10 +933,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1025,10 +1025,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1117,10 +1117,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1205,10 +1205,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1297,10 +1297,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1389,10 +1389,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1481,10 +1481,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1577,10 +1577,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1665,10 +1665,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1757,10 +1757,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1853,10 +1853,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1949,10 +1949,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2049,10 +2049,10 @@ "effects": { "barGlow": false, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2141,10 +2141,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2233,10 +2233,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2326,11 +2326,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2820,5 +2820,10 @@ "title": "Panel tests - Gauge (new)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2beta1.json index 24125f197c5..6b567f19b5e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.v2beta1.json @@ -81,10 +81,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -177,11 +177,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -277,11 +277,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -378,12 +378,12 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "sparkline": false, "spotlight": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -480,11 +480,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -580,11 +580,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -679,10 +679,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -774,10 +774,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -869,10 +869,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -964,10 +964,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1059,10 +1059,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1154,10 +1154,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": false, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1245,10 +1245,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1340,10 +1340,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1435,10 +1435,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1530,10 +1530,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1629,10 +1629,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1720,10 +1720,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1815,10 +1815,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1914,10 +1914,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2013,10 +2013,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": true, "rounded": false, "spotlight": false }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2116,10 +2116,10 @@ "effects": { "barGlow": false, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2211,10 +2211,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2306,10 +2306,10 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": false, "rounded": true, "spotlight": true }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2402,11 +2402,11 @@ "effects": { "barGlow": true, "centerGlow": true, + "gradient": true, "rounded": true, "spotlight": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2896,5 +2896,10 @@ "title": "Panel tests - Gauge (new)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v1beta1.json index ddad9707104..959f0193ad6 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v1beta1.json @@ -961,10 +961,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2alpha1.json index b485fbf84ed..66b29e88d13 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2alpha1.json @@ -864,10 +864,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1614,5 +1614,10 @@ "title": "Panel tests - Old gauge to new", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2beta1.json index 304058565f1..b870d0a91ad 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.v2beta1.json @@ -901,10 +901,10 @@ "effects": { "barGlow": false, "centerGlow": false, + "gradient": false, "rounded": false, "spotlight": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1666,5 +1666,10 @@ "title": "Panel tests - Old gauge to new", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2alpha1.json index fe6b8e09647..8f6ae2065e2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2alpha1.json @@ -671,5 +671,10 @@ "title": "Geomap - color field Copy", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2beta1.json index ccd502e8e11..e567e6dbae2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.v2beta1.json @@ -683,5 +683,10 @@ "title": "Geomap - color field Copy", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2alpha1.json index f55700a79cd..135c8d98558 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2alpha1.json @@ -201,5 +201,10 @@ "title": "Panel Tests - Geomap Photo Layer", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2beta1.json index 4a7594b73a9..ce7d1c02a17 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.v2beta1.json @@ -205,5 +205,10 @@ "title": "Panel Tests - Geomap Photo Layer", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2alpha1.json index 92f520e0857..32cc829050d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2alpha1.json @@ -341,5 +341,10 @@ "title": "Panel Tests - Geomap Fit to Data - Multiple Layer Types", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2beta1.json index 82b86117485..758836fdd75 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.v2beta1.json @@ -347,5 +347,10 @@ "title": "Panel Tests - Geomap Fit to Data - Multiple Layer Types", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2alpha1.json index 87e64b32650..f6a93ab505e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2alpha1.json @@ -173,5 +173,10 @@ "title": "Panel Tests - Geomap geohash transformer", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2beta1.json index 5184ebb6680..376a2e0b52a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.v2beta1.json @@ -177,5 +177,10 @@ "title": "Panel Tests - Geomap geohash transformer", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2alpha1.json index 6ae35916b91..aecc89871f9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2alpha1.json @@ -457,5 +457,10 @@ "title": "Panel Tests - Geomap 9.1", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2beta1.json index a385b59567b..0bbab3d3711 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.v2beta1.json @@ -467,5 +467,10 @@ "title": "Panel Tests - Geomap 9.1", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2alpha1.json index f7374506e86..d4c2a4a8145 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2alpha1.json @@ -538,5 +538,10 @@ "title": "Panel Tests - Geomap Multi Layers", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2beta1.json index b244899e6d4..4726ddb6e4b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.v2beta1.json @@ -545,5 +545,10 @@ "title": "Panel Tests - Geomap Multi Layers", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2alpha1.json index 517c35c8e98..209afba2ff2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2alpha1.json @@ -594,5 +594,10 @@ "title": "Panel Tests - Geomap", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2beta1.json index 2d8549a8d89..4fdd338e7bd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.v2beta1.json @@ -607,5 +607,10 @@ "title": "Panel Tests - Geomap", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2alpha1.json index 44ab8d372ff..12c84560a90 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2alpha1.json @@ -310,5 +310,10 @@ "title": "Panel Tests - Graph - Gradient Area Fills", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2beta1.json index 6beb26d4e70..9ae939f5d4f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.v2beta1.json @@ -320,5 +320,10 @@ "title": "Panel Tests - Graph - Gradient Area Fills", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2alpha1.json index 4628e8234f0..e0203a652e2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2alpha1.json @@ -971,5 +971,10 @@ "title": "Panel Tests - shared tooltips", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2beta1.json index 540a9010d55..fe835b1b44e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.v2beta1.json @@ -990,5 +990,10 @@ "title": "Panel Tests - shared tooltips", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2alpha1.json index 2dd4dd48907..e09a03ec44a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2alpha1.json @@ -351,5 +351,10 @@ "title": "Panel Tests - Graph Time Regions", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2beta1.json index 7a1aa009f08..f6eaa477161 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.v2beta1.json @@ -368,5 +368,10 @@ "title": "Panel Tests - Graph Time Regions", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2alpha1.json index e251ae6c6db..e67e659b5e8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2alpha1.json @@ -1752,5 +1752,10 @@ "title": "Panel Tests - Graph", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2beta1.json index a87d37bc099..7a94d1842a5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.v2beta1.json @@ -1875,5 +1875,10 @@ "title": "Panel Tests - Graph", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2alpha1.json index 33b1bd246ee..25ef1324bc4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2alpha1.json @@ -623,5 +623,10 @@ "title": "Panel Tests - Graph - Y axis ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2beta1.json index bf16659bc49..4534fe38da4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.v2beta1.json @@ -643,5 +643,10 @@ "title": "Panel Tests - Graph - Y axis ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2alpha1.json index a5567148d48..fb399db27f9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2alpha1.json @@ -1113,5 +1113,10 @@ "title": "Heatmap calculate (log)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2beta1.json index 32816087c1d..1721f0b1c1f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.v2beta1.json @@ -1133,5 +1133,10 @@ "title": "Heatmap calculate (log)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2alpha1.json index 69764686c12..4873fe5fc74 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2alpha1.json @@ -414,5 +414,10 @@ "title": "Legacy heatmap", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2beta1.json index 7450f5af8ed..2e11457ec4d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.v2beta1.json @@ -432,5 +432,10 @@ "title": "Legacy heatmap", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json index fdcb368edc4..2eb67e36f2f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json @@ -542,5 +542,10 @@ "title": "Heatmap X axis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json index b6d51918cbc..acba4cedbc2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json @@ -554,5 +554,10 @@ "title": "Heatmap X axis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json index cc15b1c4c91..57fbcad9d99 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json @@ -1243,5 +1243,10 @@ "title": "Panel Tests - Histogram", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json index 81e56c3d318..5b2ee8d8df2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json @@ -1276,5 +1276,10 @@ "title": "Panel Tests - Histogram", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2alpha1.json index 2efffd9390c..2277a05c6bb 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2alpha1.json @@ -193,5 +193,10 @@ "title": "Panel - Panel Library", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2beta1.json index 20438d25434..2f61f5977d3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.v2beta1.json @@ -197,5 +197,10 @@ "title": "Panel - Panel Library", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2alpha1.json index 742bed31cf6..84c03baac66 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2alpha1.json @@ -770,5 +770,10 @@ "title": "Panel Tests - Pie chart", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2beta1.json index b79118ec1bb..4e946c6aee9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.v2beta1.json @@ -786,5 +786,10 @@ "title": "Panel Tests - Pie chart", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2alpha1.json index 97d355f227a..68c7c2b6529 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2alpha1.json @@ -424,5 +424,10 @@ "title": "Panel Tests - Polystat", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2beta1.json index 46644077d0c..f72f1e3b485 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.v2beta1.json @@ -457,5 +457,10 @@ "title": "Panel Tests - Polystat", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2alpha1.json index acb35da6bb8..2074ff4fa12 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2alpha1.json @@ -2768,5 +2768,10 @@ "title": "Panel Tests - Stat", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2beta1.json index fb9f810cfaa..98b11ebadb3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.v2beta1.json @@ -2883,5 +2883,10 @@ "title": "Panel Tests - Stat", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json index 44db8ddd292..d90c8dc52cd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json @@ -1498,5 +1498,10 @@ "title": "StatusHistory - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json index 513bc4d476c..7aaa0fff33a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json @@ -1537,5 +1537,10 @@ "title": "StatusHistory - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json index 2697100f4cd..540f0d9e54d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json @@ -1893,5 +1893,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json index ec3f1d1ed5c..6c9aa023163 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json @@ -1939,5 +1939,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json index d2ec399b434..bccce10d162 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json @@ -2192,5 +2192,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json index a2ea184c554..5e186ef1443 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json @@ -2227,5 +2227,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2alpha1.json index 2bbd6a111bc..4863b46eeb3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2alpha1.json @@ -178,5 +178,10 @@ "title": "Panel Tests - Table - Markdown", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2beta1.json index 4a1f3a61afd..1964dd78543 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.v2beta1.json @@ -181,5 +181,10 @@ "title": "Panel Tests - Table - Markdown", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2alpha1.json index e637af09f52..cb0c2bbc16d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2alpha1.json @@ -3000,5 +3000,10 @@ "title": "Panel Tests - Table - Pagination", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2beta1.json index bc15692c4cb..d79b6c9e68b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.v2beta1.json @@ -3018,5 +3018,10 @@ "title": "Panel Tests - Table - Pagination", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json index 78012512792..e5e260fd150 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json @@ -824,5 +824,10 @@ "title": "Panel Tests - Table - Sparklines", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json index 352bab0c09b..ac15a298939 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json @@ -846,5 +846,10 @@ "title": "Panel Tests - Table - Sparklines", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2alpha1.json index 9d3a3f84f61..2bcedabe196 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2alpha1.json @@ -426,5 +426,10 @@ "title": "Panel Tests - Table", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2beta1.json index 2b97a31452a..2d84b370019 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.v2beta1.json @@ -451,5 +451,10 @@ "title": "Panel Tests - Table", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2alpha1.json index 25624e5c6d1..4e35b317cd9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2alpha1.json @@ -1252,5 +1252,10 @@ "title": "Panel Tests - React Table", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2beta1.json index b8d5f8acae3..8911415d0fc 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.v2beta1.json @@ -1280,5 +1280,10 @@ "title": "Panel Tests - React Table", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json index b8e6725ca5a..1b6348c35d5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json @@ -2507,5 +2507,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json index 4a7ea3bcf33..a77140c5beb 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json @@ -2550,5 +2550,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2alpha1.json index e0299a0be0f..3ac4d6901f4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2alpha1.json @@ -431,5 +431,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2beta1.json index a93a6c13852..6e8243f3131 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.v2beta1.json @@ -450,5 +450,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2alpha1.json index 15877291ee7..cb30e79d196 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2alpha1.json @@ -356,5 +356,10 @@ "title": "Panel Tests - StateTimeline - multiple frames with endTime", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2beta1.json index 1d857be3ea5..34c3f244b54 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.v2beta1.json @@ -365,5 +365,10 @@ "title": "Panel Tests - StateTimeline - multiple frames with endTime", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2alpha1.json index 7a2c436af82..13303a094ec 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2alpha1.json @@ -263,5 +263,10 @@ "title": "Panel Tests - StateTimeline - multiple frames with nulls", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2beta1.json index 4b14dcf2810..7d8376e9106 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.v2beta1.json @@ -267,5 +267,10 @@ "title": "Panel Tests - StateTimeline - multiple frames with nulls", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2alpha1.json index 522ec817718..82500d25a30 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2alpha1.json @@ -670,5 +670,10 @@ "title": "Timeline Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2beta1.json index 8e01aece508..89c7db392ea 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.v2beta1.json @@ -699,5 +699,10 @@ "title": "Timeline Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2alpha1.json index 7c8170cbcf8..9b4b2edfb80 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2alpha1.json @@ -548,5 +548,10 @@ "title": "Timeline Modes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2beta1.json index f5f4d53dba6..35d2643cc81 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.v2beta1.json @@ -566,5 +566,10 @@ "title": "Timeline Modes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json index ff492690353..8dff3c34ccf 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json @@ -1498,5 +1498,10 @@ "title": "StateTimeline - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json index 6dcd28290db..3baa3d21130 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json @@ -1537,5 +1537,10 @@ "title": "StateTimeline - Thresholds \u0026 Mappings", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2alpha1.json index f18b9741878..6f3390c4d11 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2alpha1.json @@ -790,5 +790,10 @@ "title": "Panel Tests - TimeSeries - bars high density (stroke + fill)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2beta1.json index 797c81e39f0..00de0e2092b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.v2beta1.json @@ -805,5 +805,10 @@ "title": "Panel Tests - TimeSeries - bars high density (stroke + fill)", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2alpha1.json index d51c3443680..2d0e6b76950 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2alpha1.json @@ -1374,5 +1374,10 @@ "title": "Panel Tests - Graph NG - By value color schemes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2beta1.json index cc2f62859f9..a3f729f9c5f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.v2beta1.json @@ -1403,5 +1403,10 @@ "title": "Panel Tests - Graph NG - By value color schemes", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2alpha1.json index 766832b0cb7..ac083165532 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2alpha1.json @@ -1067,5 +1067,10 @@ "title": "Panel Tests - Timeseries - Supported input formats", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2beta1.json index da1784c2b52..25ed3a1cf9b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.v2beta1.json @@ -1076,5 +1076,10 @@ "title": "Panel Tests - Timeseries - Supported input formats", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json index 7ad3c386000..5b43876c65f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json @@ -799,5 +799,10 @@ "title": "Panel Tests - Graph NG - Gradient Area Fills", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json index 4def562428b..ca96f9d5720 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json @@ -813,5 +813,10 @@ "title": "Panel Tests - Graph NG - Gradient Area Fills", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json index 0f5a4c7473e..74cba148009 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json @@ -1215,5 +1215,10 @@ "title": "Panel Tests - GraphNG - Hue Gradients", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json index e7e54fd938b..7e64bc79ef3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json @@ -1235,5 +1235,10 @@ "title": "Panel Tests - GraphNG - Hue Gradients", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2alpha1.json index 9bf92a90234..718a96c265c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2alpha1.json @@ -4362,5 +4362,10 @@ "title": "Panel Tests - Graph NG - Discrete panels", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2beta1.json index 3678e2c1bb8..187a377bdf1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.v2beta1.json @@ -4462,5 +4462,10 @@ "title": "Panel Tests - Graph NG - Discrete panels", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2alpha1.json index 7bfea9b01ad..f1bff310950 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2alpha1.json @@ -660,5 +660,10 @@ "title": "Panel Tests - Timeseries - Out of range", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2beta1.json index b0ee8fe1273..e868bafcffd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.v2beta1.json @@ -676,5 +676,10 @@ "title": "Panel Tests - Timeseries - Out of range", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2alpha1.json index 19003b3c112..dbe2ae22ebd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2alpha1.json @@ -821,5 +821,10 @@ "title": "Panel Tests - shared tooltips cursor positioning", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2beta1.json index 17006d8b0ac..5763494659d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.v2beta1.json @@ -839,5 +839,10 @@ "title": "Panel Tests - shared tooltips cursor positioning", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2alpha1.json index 91ebfa89a29..3c8700d1c0a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2alpha1.json @@ -2849,5 +2849,10 @@ "title": "Panel Tests - Graph NG - softMin/softMax", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2beta1.json index b616f35f02f..5cd6604807a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.v2beta1.json @@ -2887,5 +2887,10 @@ "title": "Panel Tests - Graph NG - softMin/softMax", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2alpha1.json index d5de67f8b7e..59b356d1a36 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2alpha1.json @@ -1521,5 +1521,10 @@ "title": "Panel Tests - TimeSeries - stacking", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2beta1.json index b14bc2dfae4..07c5f127c32 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.v2beta1.json @@ -1566,5 +1566,10 @@ "title": "Panel Tests - TimeSeries - stacking", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2alpha1.json index c37056e01ec..4c92eef28c1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2alpha1.json @@ -4629,5 +4629,10 @@ "title": "TimeSeries \u0026 BarChart Stacking", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2beta1.json index 200a8362e4e..486f41cdce0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.v2beta1.json @@ -4818,5 +4818,10 @@ "title": "TimeSeries \u0026 BarChart Stacking", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2alpha1.json index 99bf0a4bba4..c9a9c26558e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2alpha1.json @@ -1185,5 +1185,10 @@ "title": "Panel Tests - GraphNG Thresholds", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2beta1.json index d1f32f0900d..55ed3cf155c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.v2beta1.json @@ -1211,5 +1211,10 @@ "title": "Panel Tests - GraphNG Thresholds", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2alpha1.json index cf0c37a3a79..43271c576dd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2alpha1.json @@ -735,5 +735,10 @@ "title": "Panel Tests - GraphNG - Time Axis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2beta1.json index f106b3c8e1a..3674472eb53 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.v2beta1.json @@ -749,5 +749,10 @@ "title": "Panel Tests - GraphNG - Time Axis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2alpha1.json index 4a6a5b03287..27afd1ec327 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2alpha1.json @@ -1095,5 +1095,10 @@ "title": "Zero Decimals Y Ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2beta1.json index 0d41fc01b8f..ef2241fb8f3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.v2beta1.json @@ -1121,5 +1121,10 @@ "title": "Zero Decimals Y Ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2alpha1.json index d23c438178c..40bf202a1ae 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2alpha1.json @@ -1722,5 +1722,10 @@ "title": "Panel Tests - Graph NG - Y axis ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2beta1.json index 923573205a8..6e3377b4cc4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.v2beta1.json @@ -1765,5 +1765,10 @@ "title": "Panel Tests - Graph NG - Y axis ticks", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json index cb50db62a68..e50b453076a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json @@ -4371,5 +4371,10 @@ "title": "Panel Tests - Graph NG", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json index 249af246ac9..65105663c85 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json @@ -4445,5 +4445,10 @@ "title": "Panel Tests - Graph NG", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2alpha1.json index dda9209aa94..1439756b694 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2alpha1.json @@ -270,5 +270,10 @@ "title": "Panel Tests - Trend", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2beta1.json index 11683b31b98..65de2a79cf4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.v2beta1.json @@ -274,5 +274,10 @@ "title": "Panel Tests - Trend", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2alpha1.json index 53122268d1d..76278b42b57 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2alpha1.json @@ -1785,5 +1785,10 @@ "title": "Panel Tests - XY Chart Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2beta1.json index a4c1b86c07e..70a7b3b7537 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.v2beta1.json @@ -1825,5 +1825,10 @@ "title": "Panel Tests - XY Chart Demo", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2alpha1.json index 33d0b2ee9b2..ccb0c353c72 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2alpha1.json @@ -2478,5 +2478,10 @@ "title": "Panel Tests - XY Chart migrations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2beta1.json index a43b5c5e1e9..add31528134 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.v2beta1.json @@ -2515,5 +2515,10 @@ "title": "Panel Tests - XY Chart migrations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json index 7583af8160e..1d60f0ef9bf 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json @@ -885,5 +885,10 @@ "title": "XYChart tooltip color test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json index e10e9fc65ff..5a46646474d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json @@ -907,5 +907,10 @@ "title": "XYChart tooltip color test", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2alpha1.json index 38e58c03f3a..d203fb8e357 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2alpha1.json @@ -150,5 +150,10 @@ "title": "Mostly blank dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2beta1.json index 45c5b980f7f..8f1a85fee5f 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.v2beta1.json @@ -153,5 +153,10 @@ "title": "Mostly blank dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2alpha1.json index a8c0c9f5617..7e915f70e20 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2alpha1.json @@ -250,5 +250,10 @@ "title": "Panel Tests - Relative time zone support", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2beta1.json index e8b38167a2a..545a6a234b4 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.v2beta1.json @@ -257,5 +257,10 @@ "title": "Panel Tests - Relative time zone support", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2alpha1.json index a738ad9daba..c8314d47e25 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2alpha1.json @@ -851,5 +851,10 @@ "title": "Panel tests - Slow Queries \u0026 Annotations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2beta1.json index 2d018bf0db4..90ce6da65a3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.v2beta1.json @@ -894,5 +894,10 @@ "title": "Panel tests - Slow Queries \u0026 Annotations", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2alpha1.json index 027ae6f3352..5fd3f355cbb 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2alpha1.json @@ -3416,5 +3416,10 @@ "title": "A tall dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2beta1.json index a225f2e7d81..b0d10fd280b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.v2beta1.json @@ -3517,5 +3517,10 @@ "title": "A tall dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2alpha1.json index 0d6468c31f1..c3413246704 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2alpha1.json @@ -532,5 +532,10 @@ "title": "Panel Tests - Time zone support", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2beta1.json index 348f2876085..b2146b3b82a 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.v2beta1.json @@ -566,5 +566,10 @@ "title": "Panel Tests - Time zone support", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2alpha1.json index 8808e6d73dc..a0e87e7d5f2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2alpha1.json @@ -1014,5 +1014,10 @@ "title": "Transforms - Config from query", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2beta1.json index 69930ae2e77..8134c573a69 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.v2beta1.json @@ -1040,5 +1040,10 @@ "title": "Transforms - Config from query", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2alpha1.json index 469fca5d2aa..a611e5b0db2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2alpha1.json @@ -418,5 +418,10 @@ "title": "Transforms - Test extractFields JSON", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2beta1.json index 396cbc7348b..e1229efccac 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.v2beta1.json @@ -428,5 +428,10 @@ "title": "Transforms - Test extractFields JSON", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json index 9202330a572..056fdc62383 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json @@ -227,5 +227,10 @@ "title": "Transforms - Filters", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json index c9d1b8f11c4..57c5559add1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json @@ -233,5 +233,10 @@ "title": "Transforms - Filters", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2alpha1.json index 9797be322d6..cbd2e994f15 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2alpha1.json @@ -865,5 +865,10 @@ "title": "Transforms - Join by field", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2beta1.json index ab0fb924f23..8fc053627fd 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.v2beta1.json @@ -884,5 +884,10 @@ "title": "Transforms - Join by field", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2alpha1.json index 99353757d46..a10f2f6ed00 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2alpha1.json @@ -510,5 +510,10 @@ "title": "Transforms - Join by labels", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2beta1.json index fdc1b53ff36..73b06c4be7e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.v2beta1.json @@ -526,5 +526,10 @@ "title": "Transforms - Join by labels", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2alpha1.json index 22f053d066c..cab0fbea1e7 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2alpha1.json @@ -1348,5 +1348,10 @@ "title": "Transforms - Regression analysis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2beta1.json index 0d00e3f1021..464769f0e9e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.v2beta1.json @@ -1371,5 +1371,10 @@ "title": "Transforms - Regression analysis", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2alpha1.json index fdea0031183..08b35165f79 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2alpha1.json @@ -741,5 +741,10 @@ "title": "Transforms - Reuse dashboard queries", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2beta1.json index 79002c2d12c..d8db8e1ada9 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.v2beta1.json @@ -756,5 +756,10 @@ "title": "Transforms - Reuse dashboard queries", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2alpha1.json index 4d47c35593c..f89d509cae8 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2alpha1.json @@ -963,5 +963,10 @@ "title": "Transforms - Rows to fields", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2beta1.json index ddeb72b78f0..234f5dcbd63 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.v2beta1.json @@ -983,5 +983,10 @@ "title": "Transforms - Rows to fields", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2alpha1.json index b9aea52ad62..315164a75a3 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2alpha1.json @@ -1578,5 +1578,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2beta1.json index 0bd3a37b2d8..94f898bfa16 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.v2beta1.json @@ -1624,5 +1624,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2alpha1.json index d50e38918d2..2a6500f86db 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2alpha1.json @@ -379,5 +379,10 @@ "title": "Grafana Dev Overview \u0026 Home", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2beta1.json index dc06779b135..0580a517cb2 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.v2beta1.json @@ -386,5 +386,10 @@ "title": "Grafana Dev Overview \u0026 Home", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v0alpha1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2alpha1.json index 387ba145494..5f94f0d555b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2alpha1.json @@ -335,5 +335,10 @@ "title": "Annotation Conversions Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2beta1.json index a4dde4d26ac..c728d8dd513 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.annotation-conversions.v2beta1.json @@ -346,5 +346,10 @@ "title": "Annotation Conversions Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2alpha1.json index 3e9132d9ea9..4de46378b53 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2alpha1.json @@ -200,5 +200,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2beta1.json index 951eeaac0f6..7576414d605 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.dashboard-properties.v2beta1.json @@ -205,5 +205,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2alpha1.json index a614f32dda6..a9b876424be 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2alpha1.json @@ -165,5 +165,10 @@ "title": "Library Panel Repeat Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2beta1.json index d06d12848e6..8b2eb35cef1 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.library-panel-repeat-options.v2beta1.json @@ -165,5 +165,10 @@ "title": "Library Panel Repeat Options Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2alpha1.json index 9e0d5378f9b..ae10c4f3807 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2alpha1.json @@ -501,5 +501,10 @@ "title": "Panel Conversions Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2beta1.json index 8eac23d96c0..910dbbe3e1c 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-conversions.v2beta1.json @@ -510,5 +510,10 @@ "title": "Panel Conversions Test Dashboard", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2alpha1.json index 5a0a8e074e6..55b0af155ff 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2alpha1.json @@ -201,5 +201,10 @@ "title": "Panel ds inheritance ", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2beta1.json index 3a13853c08e..48ec1ca758b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.panel-datasource-type-datasource.v2beta1.json @@ -204,5 +204,10 @@ "title": "Panel ds inheritance ", "variables": [] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2alpha1.json index c51582691b9..040513a3ce5 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2alpha1.json @@ -414,5 +414,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2beta1.json index 441258438a9..cf3ff5443c0 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v1beta1.variable-conversions.v2beta1.json @@ -417,5 +417,10 @@ } ] }, - "status": {} + "status": { + "conversion": { + "failed": false, + "storedVersion": "v1beta1" + } + } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/v0.go b/apps/dashboard/pkg/migration/conversion/v0.go index 826293d9564..513f6975516 100644 --- a/apps/dashboard/pkg/migration/conversion/v0.go +++ b/apps/dashboard/pkg/migration/conversion/v0.go @@ -13,6 +13,9 @@ import ( func Convert_V0_to_V1beta1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error { if err := ConvertDashboard_V0_to_V1beta1(in, out, scope); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv1.APIVERSION + out.Kind = in.Kind out.Status = dashv1.DashboardStatus{ Conversion: &dashv1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -22,12 +25,24 @@ func Convert_V0_to_V1beta1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope co } return err } + + // Set successful conversion status + out.Status = dashv1.DashboardStatus{ + Conversion: &dashv1.DashboardConversionStatus{ + StoredVersion: ptr.To(dashv0.VERSION), + Failed: false, + }, + } + return nil } func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error { v1beta1 := &dashv1.Dashboard{} if err := ConvertDashboard_V0_to_V1beta1(in, v1beta1, scope); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2alpha1.APIVERSION + out.Kind = in.Kind out.Status = dashv2alpha1.DashboardStatus{ Conversion: &dashv2alpha1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -35,7 +50,6 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s Error: ptr.To(err.Error()), }, } - // Don't return error - just set status (matches test expectations) // Ensure layout is set even on error to prevent JSON marshaling issues if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { out.Spec.Layout = dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ @@ -45,10 +59,13 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s }, } } - return nil + return err } if err := ConvertDashboard_V1beta1_to_V2alpha1(v1beta1, out, scope, dsIndexProvider, leIndexProvider); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2alpha1.APIVERSION + out.Kind = in.Kind out.Status = dashv2alpha1.DashboardStatus{ Conversion: &dashv2alpha1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -56,7 +73,6 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s Error: ptr.To(err.Error()), }, } - // Don't return error - just set status (matches test expectations) // Ensure layout is set even on error to prevent JSON marshaling issues if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { out.Spec.Layout = dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ @@ -66,7 +82,15 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s }, } } - return nil + return err + } + + // Set successful conversion status + out.Status = dashv2alpha1.DashboardStatus{ + Conversion: &dashv2alpha1.DashboardConversionStatus{ + StoredVersion: ptr.To(dashv0.VERSION), + Failed: false, + }, } return nil @@ -75,6 +99,9 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error { v1beta1 := &dashv1.Dashboard{} if err := ConvertDashboard_V0_to_V1beta1(in, v1beta1, scope); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2beta1.APIVERSION + out.Kind = in.Kind out.Status = dashv2beta1.DashboardStatus{ Conversion: &dashv2beta1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -82,11 +109,23 @@ func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, sco Error: ptr.To(err.Error()), }, } + // Ensure layout is set even on error to prevent JSON marshaling issues + if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { + out.Spec.Layout = dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + } + } return err } v2alpha1 := &dashv2alpha1.Dashboard{} if err := ConvertDashboard_V1beta1_to_V2alpha1(v1beta1, v2alpha1, scope, dsIndexProvider, leIndexProvider); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2beta1.APIVERSION + out.Kind = in.Kind out.Status = dashv2beta1.DashboardStatus{ Conversion: &dashv2beta1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -94,10 +133,22 @@ func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, sco Error: ptr.To(err.Error()), }, } + // Ensure layout is set even on error to prevent JSON marshaling issues + if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { + out.Spec.Layout = dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + } + } return err } if err := ConvertDashboard_V2alpha1_to_V2beta1(v2alpha1, out, scope); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2beta1.APIVERSION + out.Kind = in.Kind out.Status = dashv2beta1.DashboardStatus{ Conversion: &dashv2beta1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv0.VERSION), @@ -105,8 +156,25 @@ func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, sco Error: ptr.To(err.Error()), }, } + // Ensure layout is set even on error to prevent JSON marshaling issues + if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { + out.Spec.Layout = dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + } + } return err } + // Set successful conversion status + out.Status = dashv2beta1.DashboardStatus{ + Conversion: &dashv2beta1.DashboardConversionStatus{ + StoredVersion: ptr.To(dashv0.VERSION), + Failed: false, + }, + } + return nil } diff --git a/apps/dashboard/pkg/migration/conversion/v0_test.go b/apps/dashboard/pkg/migration/conversion/v0_test.go index 5566a72c767..3620ea4d099 100644 --- a/apps/dashboard/pkg/migration/conversion/v0_test.go +++ b/apps/dashboard/pkg/migration/conversion/v0_test.go @@ -54,7 +54,7 @@ func TestV0ConversionErrorHandling(t *testing.T) { }, }, { - name: "Convert_V0_to_V2alpha1 sets status on first step migration failure", + name: "Convert_V0_to_V2alpha1 returns error and sets status on first step migration failure", source: &dashv0.Dashboard{ ObjectMeta: metav1.ObjectMeta{ Namespace: "org-1", @@ -67,7 +67,7 @@ func TestV0ConversionErrorHandling(t *testing.T) { }, }, target: &dashv2alpha1.Dashboard{}, - expectError: false, // Convert_V0_to_V2alpha1 doesn't return error, just sets status + expectError: true, // Convert_V0_to_V2alpha1 now returns error for proper metrics/logging expectStatusSet: true, checkStatus: func(t *testing.T, target interface{}) { out := target.(*dashv2alpha1.Dashboard) @@ -277,7 +277,7 @@ func TestV0ConversionSecondStepErrors(t *testing.T) { leProvider := migrationtestutil.NewLibraryElementProvider() migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) - t.Run("Convert_V0_to_V2alpha1 sets status on first step error", func(t *testing.T) { + t.Run("Convert_V0_to_V2alpha1 returns error and sets status on first step error", func(t *testing.T) { // Create a dashboard that will fail v0->v1beta1 conversion // Use schemaVersion 0 which will cause migration to fail source := &dashv0.Dashboard{ @@ -295,16 +295,16 @@ func TestV0ConversionSecondStepErrors(t *testing.T) { err := Convert_V0_to_V2alpha1(source, target, nil, dsProvider, leProvider) - // Convert_V0_to_V2alpha1 doesn't return error, just sets status - require.NoError(t, err, "Convert_V0_to_V2alpha1 doesn't return error") - // Status should be set when first step fails + // Convert_V0_to_V2alpha1 returns error for proper metrics/logging + require.Error(t, err, "Convert_V0_to_V2alpha1 should return error") + // Status should also be set when first step fails require.NotNil(t, target.Status.Conversion, "Status should be set on first step error") require.True(t, target.Status.Conversion.Failed, "Failed should be true") require.NotNil(t, target.Status.Conversion.Error, "Error should be set") require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) }) - t.Run("Convert_V0_to_V2alpha1 sets status on second step error", func(t *testing.T) { + t.Run("Convert_V0_to_V2alpha1 returns error and sets status on second step error", func(t *testing.T) { // Create a dashboard that will pass v0->v1beta1 but fail v1beta1->v2alpha1 // We need to create invalid JSON structure that will cause JSON marshaling to fail // or create a dashboard with invalid structure that causes transformation to fail @@ -329,16 +329,13 @@ func TestV0ConversionSecondStepErrors(t *testing.T) { err := Convert_V0_to_V2alpha1(source, target, nil, dsProvider, leProvider) - // Convert_V0_to_V2alpha1 doesn't return error, just sets status - require.NoError(t, err, "Convert_V0_to_V2alpha1 doesn't return error") - // If second step fails, status should be set - // Note: The error might occur in first step if JSON marshal fails early, - // but we're testing that the error handling path exists - if target.Status.Conversion != nil && target.Status.Conversion.Failed { - require.True(t, target.Status.Conversion.Failed) - require.NotNil(t, target.Status.Conversion.Error) - require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) - } + // Convert_V0_to_V2alpha1 returns error for proper metrics/logging + require.Error(t, err, "Convert_V0_to_V2alpha1 should return error") + // Status should also be set + require.NotNil(t, target.Status.Conversion, "Status should be set on error") + require.True(t, target.Status.Conversion.Failed, "Failed should be true") + require.NotNil(t, target.Status.Conversion.Error, "Error should be set") + require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) }) t.Run("Convert_V0_to_V2beta1 returns error on second step failure", func(t *testing.T) { @@ -394,3 +391,177 @@ func TestV0ConversionSecondStepErrors(t *testing.T) { } }) } + +// TestV0ConversionConsistency_ErrorsMustBeReturned ensures v0 conversion functions +// return errors instead of swallowing them. This prevents metrics and logs from being silently dropped. +func TestV0ConversionConsistency_ErrorsMustBeReturned(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Create a v0 dashboard that will fail conversion (invalid schema version) + invalidV0 := &dashv0.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 0, // Invalid schema version causes migration to fail + }, + }, + } + + t.Run("Convert_V0_to_V1beta1 must return error on failure", func(t *testing.T) { + target := &dashv1.Dashboard{} + err := Convert_V0_to_V1beta1(invalidV0, target, nil) + require.Error(t, err, "Convert_V0_to_V1beta1 must return error, not swallow it") + require.True(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be true") + }) + + t.Run("Convert_V0_to_V2alpha1 must return error on failure", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + err := Convert_V0_to_V2alpha1(invalidV0, target, nil, dsProvider, leProvider) + require.Error(t, err, "Convert_V0_to_V2alpha1 must return error, not swallow it") + require.True(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be true") + }) + + t.Run("Convert_V0_to_V2beta1 must return error on failure", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + err := Convert_V0_to_V2beta1(invalidV0, target, nil, dsProvider, leProvider) + require.Error(t, err, "Convert_V0_to_V2beta1 must return error, not swallow it") + require.True(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be true") + }) +} + +// TestV0ConversionConsistency_SuccessStatusMustBeSet ensures v0 conversion functions +// set Status.Conversion with Failed=false on successful conversion. +func TestV0ConversionConsistency_SuccessStatusMustBeSet(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Create a valid v0 dashboard + validV0 := &dashv0.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 42, + }, + }, + } + + t.Run("Convert_V0_to_V1beta1 must set success status", func(t *testing.T) { + target := &dashv1.Dashboard{} + err := Convert_V0_to_V1beta1(validV0, target, nil) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) + }) + + t.Run("Convert_V0_to_V2alpha1 must set success status", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + err := Convert_V0_to_V2alpha1(validV0, target, nil, dsProvider, leProvider) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) + }) + + t.Run("Convert_V0_to_V2beta1 must set success status", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + err := Convert_V0_to_V2beta1(validV0, target, nil, dsProvider, leProvider) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + require.Equal(t, dashv0.VERSION, *target.Status.Conversion.StoredVersion) + }) +} + +// TestV0ConversionConsistency_ObjectMetaMustBeSetOnError ensures v0 conversion functions +// set ObjectMeta, APIVersion, and Kind on the target even when conversion fails. +func TestV0ConversionConsistency_ObjectMetaMustBeSetOnError(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + invalidV0 := &dashv0.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + UID: "test-uid", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 0, + }, + }, + } + + t.Run("Convert_V0_to_V1beta1 must set ObjectMeta on error", func(t *testing.T) { + target := &dashv1.Dashboard{} + _ = Convert_V0_to_V1beta1(invalidV0, target, nil) + require.Equal(t, invalidV0.Name, target.Name, "Name must be set on error") + require.Equal(t, invalidV0.Namespace, target.Namespace, "Namespace must be set on error") + require.Equal(t, dashv1.APIVERSION, target.APIVersion, "APIVersion must be set on error") + }) + + t.Run("Convert_V0_to_V2alpha1 must set ObjectMeta on error", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + _ = Convert_V0_to_V2alpha1(invalidV0, target, nil, dsProvider, leProvider) + require.Equal(t, invalidV0.Name, target.Name, "Name must be set on error") + require.Equal(t, invalidV0.Namespace, target.Namespace, "Namespace must be set on error") + require.Equal(t, dashv2alpha1.APIVERSION, target.APIVersion, "APIVersion must be set on error") + }) + + t.Run("Convert_V0_to_V2beta1 must set ObjectMeta on error", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + _ = Convert_V0_to_V2beta1(invalidV0, target, nil, dsProvider, leProvider) + require.Equal(t, invalidV0.Name, target.Name, "Name must be set on error") + require.Equal(t, invalidV0.Namespace, target.Namespace, "Namespace must be set on error") + require.Equal(t, dashv2beta1.APIVERSION, target.APIVersion, "APIVersion must be set on error") + }) +} + +// TestV0ConversionConsistency_LayoutMustBeSetOnError ensures v2alpha1 and v2beta1 targets +// have a default layout set even when conversion fails to prevent JSON marshaling errors. +func TestV0ConversionConsistency_LayoutMustBeSetOnError(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + invalidV0 := &dashv0.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 0, + }, + }, + } + + t.Run("Convert_V0_to_V2alpha1 must set default layout on error", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + _ = Convert_V0_to_V2alpha1(invalidV0, target, nil, dsProvider, leProvider) + require.NotNil(t, target.Spec.Layout.GridLayoutKind, "GridLayoutKind must be set on error to prevent JSON marshaling issues") + }) + + t.Run("Convert_V0_to_V2beta1 must set default layout on error", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + _ = Convert_V0_to_V2beta1(invalidV0, target, nil, dsProvider, leProvider) + require.NotNil(t, target.Spec.Layout.GridLayoutKind, "GridLayoutKind must be set on error to prevent JSON marshaling issues") + }) +} diff --git a/apps/dashboard/pkg/migration/conversion/v1.go b/apps/dashboard/pkg/migration/conversion/v1.go index a6f2836b464..ac538e01e02 100644 --- a/apps/dashboard/pkg/migration/conversion/v1.go +++ b/apps/dashboard/pkg/migration/conversion/v1.go @@ -29,6 +29,9 @@ func Convert_V1beta1_to_V0(in *dashv1.Dashboard, out *dashv0.Dashboard, scope co func Convert_V1beta1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error { if err := ConvertDashboard_V1beta1_to_V2alpha1(in, out, scope, dsIndexProvider, leIndexProvider); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2alpha1.APIVERSION + out.Kind = in.Kind out.Status = dashv2alpha1.DashboardStatus{ Conversion: &dashv2alpha1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv1.VERSION), @@ -36,7 +39,6 @@ func Convert_V1beta1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboa Error: ptr.To(err.Error()), }, } - // Don't return error - just set status (matches test expectations and V0 pattern for Convert_V0_to_V2alpha1) // Ensure layout is set even on error to prevent JSON marshaling issues if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { out.Spec.Layout = dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ @@ -46,7 +48,7 @@ func Convert_V1beta1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboa }, } } - return nil + return err } // We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail. @@ -59,12 +61,23 @@ func Convert_V1beta1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboa } } + // Set successful conversion status + out.Status = dashv2alpha1.DashboardStatus{ + Conversion: &dashv2alpha1.DashboardConversionStatus{ + StoredVersion: ptr.To(dashv1.VERSION), + Failed: false, + }, + } + return nil } func Convert_V1beta1_to_V2beta1(in *dashv1.Dashboard, out *dashv2beta1.Dashboard, scope conversion.Scope, dsIndexProvider schemaversion.DataSourceIndexProvider, leIndexProvider schemaversion.LibraryElementIndexProvider) error { v2alpha1 := &dashv2alpha1.Dashboard{} if err := ConvertDashboard_V1beta1_to_V2alpha1(in, v2alpha1, scope, dsIndexProvider, leIndexProvider); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2beta1.APIVERSION + out.Kind = in.Kind out.Status = dashv2beta1.DashboardStatus{ Conversion: &dashv2beta1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv1.VERSION), @@ -72,10 +85,22 @@ func Convert_V1beta1_to_V2beta1(in *dashv1.Dashboard, out *dashv2beta1.Dashboard Error: ptr.To(err.Error()), }, } + // Ensure layout is set even on error to prevent JSON marshaling issues + if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { + out.Spec.Layout = dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + } + } return err } if err := ConvertDashboard_V2alpha1_to_V2beta1(v2alpha1, out, scope); err != nil { + out.ObjectMeta = in.ObjectMeta + out.APIVersion = dashv2beta1.APIVERSION + out.Kind = in.Kind out.Status = dashv2beta1.DashboardStatus{ Conversion: &dashv2beta1.DashboardConversionStatus{ StoredVersion: ptr.To(dashv1.VERSION), @@ -83,8 +108,25 @@ func Convert_V1beta1_to_V2beta1(in *dashv1.Dashboard, out *dashv2beta1.Dashboard Error: ptr.To(err.Error()), }, } + // Ensure layout is set even on error to prevent JSON marshaling issues + if out.Spec.Layout.GridLayoutKind == nil && out.Spec.Layout.RowsLayoutKind == nil { + out.Spec.Layout = dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + } + } return err } + // Set successful conversion status + out.Status = dashv2beta1.DashboardStatus{ + Conversion: &dashv2beta1.DashboardConversionStatus{ + StoredVersion: ptr.To(dashv1.VERSION), + Failed: false, + }, + } + return nil } diff --git a/apps/dashboard/pkg/migration/conversion/v1_test.go b/apps/dashboard/pkg/migration/conversion/v1_test.go index 28cf6e49a3b..6eca186d3ee 100644 --- a/apps/dashboard/pkg/migration/conversion/v1_test.go +++ b/apps/dashboard/pkg/migration/conversion/v1_test.go @@ -21,9 +21,8 @@ func TestV1ConversionErrorHandling(t *testing.T) { leProvider := migrationtestutil.NewLibraryElementProvider() migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) - t.Run("Convert_V1beta1_to_V2alpha1 sets status on conversion error", func(t *testing.T) { - // Create a dashboard that will cause conversion to fail - // We can use an invalid dashboard structure + t.Run("Convert_V1beta1_to_V2alpha1 sets status on successful conversion", func(t *testing.T) { + // Create a simple dashboard that will convert successfully source := &dashv1.Dashboard{ ObjectMeta: metav1.ObjectMeta{ Namespace: "org-1", @@ -31,7 +30,6 @@ func TestV1ConversionErrorHandling(t *testing.T) { Spec: common.Unstructured{ Object: map[string]interface{}{ "title": "test dashboard", - // Missing required fields that might cause conversion to fail }, }, } @@ -39,15 +37,14 @@ func TestV1ConversionErrorHandling(t *testing.T) { err := Convert_V1beta1_to_V2alpha1(source, target, nil, dsProvider, leProvider) - // Convert_V1beta1_to_V2alpha1 doesn't return error, just sets status - require.NoError(t, err, "Convert_V1beta1_to_V2alpha1 doesn't return error") + // Conversion should succeed + require.NoError(t, err) // Layout should always be set require.NotNil(t, target.Spec.Layout.GridLayoutKind) - // If conversion failed, status should be set - if target.Status.Conversion != nil { - require.True(t, target.Status.Conversion.Failed) - require.NotNil(t, target.Status.Conversion.Error) - } + // Status should be set with success + require.NotNil(t, target.Status.Conversion) + require.False(t, target.Status.Conversion.Failed) + require.Equal(t, dashv1.VERSION, *target.Status.Conversion.StoredVersion) }) t.Run("Convert_V1beta1_to_V2beta1 returns error on first step failure", func(t *testing.T) { @@ -126,3 +123,156 @@ func TestV1ConversionErrorHandling(t *testing.T) { } }) } + +// TestV1ConversionConsistency_ErrorsMustBeReturned ensures v1 conversion functions +// return errors instead of swallowing them. This prevents metrics and logs from being silently dropped. +func TestV1ConversionConsistency_ErrorsMustBeReturned(t *testing.T) { + leProvider := migrationtestutil.NewLibraryElementProvider() + + // Create a valid v1 dashboard - the nil dsProvider will cause conversion to fail + validV1 := &dashv1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 42, + }, + }, + } + + t.Run("Convert_V1beta1_to_V2alpha1 must return error on failure", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2alpha1(validV1, target, nil, nil, leProvider) + require.Error(t, err, "Convert_V1beta1_to_V2alpha1 must return error, not swallow it") + require.True(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be true") + }) + + t.Run("Convert_V1beta1_to_V2beta1 must return error on failure", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2beta1(validV1, target, nil, nil, leProvider) + require.Error(t, err, "Convert_V1beta1_to_V2beta1 must return error, not swallow it") + require.True(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be true") + }) +} + +// TestV1ConversionConsistency_SuccessStatusMustBeSet ensures v1 conversion functions +// set Status.Conversion with Failed=false on successful conversion. +func TestV1ConversionConsistency_SuccessStatusMustBeSet(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Create a valid v1 dashboard + validV1 := &dashv1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 42, + }, + }, + } + + t.Run("Convert_V1beta1_to_V2alpha1 must set success status", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + err := Convert_V1beta1_to_V2alpha1(validV1, target, nil, dsProvider, leProvider) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + require.Equal(t, dashv1.VERSION, *target.Status.Conversion.StoredVersion) + }) + + t.Run("Convert_V1beta1_to_V2beta1 must set success status", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + err := Convert_V1beta1_to_V2beta1(validV1, target, nil, dsProvider, leProvider) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + require.Equal(t, dashv1.VERSION, *target.Status.Conversion.StoredVersion) + }) +} + +// TestV1ConversionConsistency_ObjectMetaMustBeSetOnError ensures v1 conversion functions +// set ObjectMeta, APIVersion, and Kind on the target even when conversion fails. +func TestV1ConversionConsistency_ObjectMetaMustBeSetOnError(t *testing.T) { + leProvider := migrationtestutil.NewLibraryElementProvider() + + validV1 := &dashv1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + UID: "test-uid", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 42, + }, + }, + } + + t.Run("Convert_V1beta1_to_V2alpha1 must set ObjectMeta on error", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2alpha1(validV1, target, nil, nil, leProvider) + require.Error(t, err) + require.Equal(t, validV1.Name, target.Name, "Name must be set on error") + require.Equal(t, validV1.Namespace, target.Namespace, "Namespace must be set on error") + require.Equal(t, dashv2alpha1.APIVERSION, target.APIVersion, "APIVersion must be set on error") + }) + + t.Run("Convert_V1beta1_to_V2beta1 must set ObjectMeta on error", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2beta1(validV1, target, nil, nil, leProvider) + require.Error(t, err) + require.Equal(t, validV1.Name, target.Name, "Name must be set on error") + require.Equal(t, validV1.Namespace, target.Namespace, "Namespace must be set on error") + require.Equal(t, dashv2beta1.APIVERSION, target.APIVersion, "APIVersion must be set on error") + }) +} + +// TestV1ConversionConsistency_LayoutMustBeSetOnError ensures v2alpha1 and v2beta1 targets +// have a default layout set even when conversion fails to prevent JSON marshaling errors. +func TestV1ConversionConsistency_LayoutMustBeSetOnError(t *testing.T) { + leProvider := migrationtestutil.NewLibraryElementProvider() + + validV1 := &dashv1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "org-1", + Name: "test-dashboard", + }, + Spec: common.Unstructured{ + Object: map[string]interface{}{ + "title": "test dashboard", + "schemaVersion": 42, + }, + }, + } + + t.Run("Convert_V1beta1_to_V2alpha1 must set default layout on error", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2alpha1(validV1, target, nil, nil, leProvider) + require.Error(t, err) + require.NotNil(t, target.Spec.Layout.GridLayoutKind, "GridLayoutKind must be set on error to prevent JSON marshaling issues") + }) + + t.Run("Convert_V1beta1_to_V2beta1 must set default layout on error", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + // Pass nil dsProvider to cause conversion to fail + err := Convert_V1beta1_to_V2beta1(validV1, target, nil, nil, leProvider) + require.Error(t, err) + require.NotNil(t, target.Spec.Layout.GridLayoutKind, "GridLayoutKind must be set on error to prevent JSON marshaling issues") + }) +} diff --git a/apps/dashboard/pkg/migration/conversion/v2.go b/apps/dashboard/pkg/migration/conversion/v2.go index fa8a49e91b4..beed5679b85 100644 --- a/apps/dashboard/pkg/migration/conversion/v2.go +++ b/apps/dashboard/pkg/migration/conversion/v2.go @@ -26,8 +26,7 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s Source: in, }, } - // For errors, set status but don't return error - return nil + return err } // Convert v1beta1 → v0 @@ -43,8 +42,7 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s Source: in, }, } - // For errors, set status but don't return error - return nil + return err } // Update the stored version to reflect the original source @@ -68,9 +66,7 @@ func Convert_V2alpha1_to_V1beta1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboa Source: in, }, } - - // For errors, set status but don't return error - return nil + return err } // Set successful conversion status @@ -129,8 +125,7 @@ func Convert_V2beta1_to_V0(in *dashv2beta1.Dashboard, out *dashv0.Dashboard, sco Source: in, }, } - // For errors, set status but don't return error - return nil + return err } // Convert v1beta1 → v0 @@ -146,7 +141,7 @@ func Convert_V2beta1_to_V0(in *dashv2beta1.Dashboard, out *dashv0.Dashboard, sco Source: in, }, } - return nil + return err } // Update the stored version to reflect the original source @@ -172,8 +167,7 @@ func Convert_V2beta1_to_V1beta1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard Source: in, }, } - // For errors, set status but don't return error - return nil + return err } // Convert v2alpha1 → v1beta1 @@ -188,8 +182,7 @@ func Convert_V2beta1_to_V1beta1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard Source: in, }, } - // For errors, set status but don't return error - return nil + return err } // Set successful conversion status diff --git a/apps/dashboard/pkg/migration/conversion/v2_test.go b/apps/dashboard/pkg/migration/conversion/v2_test.go index d2010062223..3e218da9f81 100644 --- a/apps/dashboard/pkg/migration/conversion/v2_test.go +++ b/apps/dashboard/pkg/migration/conversion/v2_test.go @@ -186,3 +186,141 @@ func TestV2beta1ConversionErrorHandling(t *testing.T) { } }) } + +// TestV2ConversionConsistency_SuccessStatusMustBeSet ensures v2 conversion functions +// set Status.Conversion with Failed=false on successful conversion. +func TestV2ConversionConsistency_SuccessStatusMustBeSet(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Create valid v2alpha1 dashboard + validV2alpha1 := &dashv2alpha1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: dashv2alpha1.DashboardSpec{ + Title: "test dashboard", + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{}, + }, + }, + }, + } + + // Create valid v2beta1 dashboard + validV2beta1 := &dashv2beta1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: dashv2beta1.DashboardSpec{ + Title: "test dashboard", + Layout: dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + }, + }, + } + + t.Run("Convert_V2alpha1_to_V1beta1 must set success status", func(t *testing.T) { + target := &dashv1.Dashboard{} + err := Convert_V2alpha1_to_V1beta1(validV2alpha1, target, nil) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + }) + + t.Run("Convert_V2alpha1_to_V2beta1 must set success status", func(t *testing.T) { + target := &dashv2beta1.Dashboard{} + err := Convert_V2alpha1_to_V2beta1(validV2alpha1, target, nil) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + }) + + t.Run("Convert_V2beta1_to_V1beta1 must set success status", func(t *testing.T) { + target := &dashv1.Dashboard{} + err := Convert_V2beta1_to_V1beta1(validV2beta1, target, nil, dsProvider) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + }) + + t.Run("Convert_V2beta1_to_V2alpha1 must set success status", func(t *testing.T) { + target := &dashv2alpha1.Dashboard{} + err := Convert_V2beta1_to_V2alpha1(validV2beta1, target, nil) + require.NoError(t, err) + require.NotNil(t, target.Status.Conversion, "Status.Conversion must be set on success") + require.False(t, target.Status.Conversion.Failed, "Status.Conversion.Failed must be false on success") + require.NotNil(t, target.Status.Conversion.StoredVersion, "StoredVersion must be set") + }) +} + +// TestV2ConversionConsistency_ErrorsMustBeReturned ensures v2 conversion functions +// return errors instead of swallowing them. This prevents metrics and logs from being silently dropped. +func TestV2ConversionConsistency_ErrorsMustBeReturned(t *testing.T) { + dsProvider := migrationtestutil.NewDataSourceProvider(migrationtestutil.StandardTestConfig) + leProvider := migrationtestutil.NewLibraryElementProvider() + migration.Initialize(dsProvider, leProvider, migration.DefaultCacheTTL) + + // Note: v2 conversions are harder to make fail since they don't go through schema migration. + // These tests verify that IF an error occurs, it is returned (not swallowed). + // The existing tests already cover this, but we add explicit assertions here. + + t.Run("Convert_V2alpha1_to_V2beta1 returns error on conversion failure", func(t *testing.T) { + // Valid dashboard should succeed + source := &dashv2alpha1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: dashv2alpha1.DashboardSpec{ + Title: "test dashboard", + Layout: dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2alpha1.DashboardGridLayoutSpec{}, + }, + }, + }, + } + target := &dashv2beta1.Dashboard{} + err := Convert_V2alpha1_to_V2beta1(source, target, nil) + // This should succeed, verifying the function works + require.NoError(t, err) + require.False(t, target.Status.Conversion.Failed) + }) + + t.Run("Convert_V2beta1_to_V2alpha1 returns error on conversion failure", func(t *testing.T) { + // Valid dashboard should succeed + source := &dashv2beta1.Dashboard{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-dashboard", + }, + Spec: dashv2beta1.DashboardSpec{ + Title: "test dashboard", + Layout: dashv2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{ + GridLayoutKind: &dashv2beta1.DashboardGridLayoutKind{ + Kind: "GridLayout", + Spec: dashv2beta1.DashboardGridLayoutSpec{}, + }, + }, + }, + } + target := &dashv2alpha1.Dashboard{} + err := Convert_V2beta1_to_V2alpha1(source, target, nil) + // This should succeed, verifying the function works + require.NoError(t, err) + require.False(t, target.Status.Conversion.Failed) + }) +} diff --git a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go index fed2691db27..73fd97b2089 100644 --- a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go +++ b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go @@ -1059,7 +1059,8 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[ } // Convert queries (targets) - targets := make([]map[string]interface{}, 0, len(spec.Data.Spec.Queries)) + // Use []interface{} for consistency with JSON unmarshaling and other code paths + targets := make([]interface{}, 0, len(spec.Data.Spec.Queries)) for _, query := range spec.Data.Spec.Queries { target := convertPanelQueryToV1(&query) targets = append(targets, target) diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json index 863d7b2a102..c589f8b7400 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json @@ -76,9 +76,9 @@ "barGlow": false, "centerGlow": false, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -155,9 +155,9 @@ "barGlow": false, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -234,9 +234,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -313,9 +313,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -392,9 +392,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -471,9 +471,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -550,9 +550,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -642,9 +642,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -721,9 +721,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -800,9 +800,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -879,9 +879,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -975,9 +975,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1054,9 +1054,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1133,9 +1133,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1212,9 +1212,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1291,9 +1291,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1387,9 +1387,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1470,9 +1470,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1553,9 +1553,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1645,10 +1645,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1731,10 +1731,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1831,10 +1831,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1918,11 +1918,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "sparkline": false, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2005,10 +2004,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2091,10 +2090,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -2147,4 +2146,4 @@ "title": "Panel tests - Gauge (new)", "uid": "panel-tests-gauge-new", "weekStart": "" -} \ No newline at end of file +} diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json index d4dd7980100..a3de6df336a 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_old_to_new.v42.json @@ -956,9 +956,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": [ @@ -1162,4 +1162,4 @@ "title": "Panel tests - Old gauge to new", "uid": "panel-tests-old-gauge-to-new", "weekStart": "" -} \ No newline at end of file +} diff --git a/apps/iam/pkg/app/app.go b/apps/iam/pkg/app/app.go index 05216220d7e..ff2a0cc7feb 100644 --- a/apps/iam/pkg/app/app.go +++ b/apps/iam/pkg/app/app.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/prometheus/client_golang/prometheus" + "github.com/grafana/grafana-app-sdk/app" "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/operator" @@ -12,7 +14,6 @@ import ( foldersKind "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" "github.com/grafana/grafana/apps/iam/pkg/reconcilers" "github.com/grafana/grafana/pkg/services/authz" - "github.com/prometheus/client_golang/prometheus" ) var appManifestData = app.ManifestData{ @@ -78,7 +79,7 @@ func New(cfg app.Config) (app.App, error) { folderReconciler, err := reconcilers.NewFolderReconciler(reconcilers.ReconcilerConfig{ ZanzanaCfg: appSpecificConfig.ZanzanaClientCfg, Metrics: metrics, - }) + }, appSpecificConfig.MetricsRegisterer) if err != nil { return nil, fmt.Errorf("unable to create FolderReconciler: %w", err) } diff --git a/apps/iam/pkg/reconcilers/folder_reconciler.go b/apps/iam/pkg/reconcilers/folder_reconciler.go index 066637ee1e6..66a6868722d 100644 --- a/apps/iam/pkg/reconcilers/folder_reconciler.go +++ b/apps/iam/pkg/reconcilers/folder_reconciler.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -35,9 +36,9 @@ type FolderReconciler struct { metrics *ReconcilerMetrics } -func NewFolderReconciler(cfg ReconcilerConfig) (operator.Reconciler, error) { +func NewFolderReconciler(cfg ReconcilerConfig, reg prometheus.Registerer) (operator.Reconciler, error) { // Create Zanzana client - zanzanaClient, err := authz.NewRemoteZanzanaClient("*", cfg.ZanzanaCfg) + zanzanaClient, err := authz.NewRemoteZanzanaClient(cfg.ZanzanaCfg, reg) if err != nil { return nil, fmt.Errorf("unable to create zanzana client: %w", err) diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json index 9cb204da48a..f9ee5a8c4e3 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json @@ -76,9 +76,9 @@ "barGlow": false, "centerGlow": false, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -153,9 +153,9 @@ "barGlow": false, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -230,9 +230,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -307,9 +307,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -384,9 +384,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -461,9 +461,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -538,9 +538,9 @@ "barGlow": true, "centerGlow": true, "rounded": false, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -628,9 +628,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -705,9 +705,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -782,9 +782,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -859,9 +859,9 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -953,9 +953,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1030,9 +1030,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1107,9 +1107,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1184,9 +1184,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1261,9 +1261,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false }, - "gradient": "none", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1355,9 +1355,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1436,9 +1436,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1517,9 +1517,9 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": true }, - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1607,10 +1607,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "auto", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1691,10 +1691,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1789,10 +1789,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1874,11 +1874,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "sparkline": false, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "scheme", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -1959,10 +1958,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], @@ -2043,10 +2042,10 @@ "barGlow": true, "centerGlow": true, "rounded": true, - "spotlight": true + "spotlight": true, + "gradient": true }, "glow": "both", - "gradient": "hue", "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json index 61bec02a59b..b071ddff802 100644 --- a/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_old_to_new.json @@ -947,7 +947,6 @@ }, "orientation": "auto", "shape": "gauge", - "gradient": "none", "barWidthFactor": 0.5, "segmentCount": 70, "segmentSpacing": 0.3, @@ -958,7 +957,8 @@ "barGlow": false, "centerGlow": false, "rounded": false, - "spotlight": false + "spotlight": false, + "gradient": false } } }, diff --git a/docs/sources/visualizations/panels-visualizations/query-transform-data/_index.md b/docs/sources/visualizations/panels-visualizations/query-transform-data/_index.md index b6c9471e3e3..5b121028404 100644 --- a/docs/sources/visualizations/panels-visualizations/query-transform-data/_index.md +++ b/docs/sources/visualizations/panels-visualizations/query-transform-data/_index.md @@ -162,6 +162,12 @@ To access your saved queries, click **+ Add from saved queries** or **Replace wi Clicking **+ Add from saved queries** adds an additional query, while clicking **Replace with saved query** updates your existing query. +{{< admonition type="note" >}} +Users with Admin and Editor roles can create and save queries for reuse. +While Admin users can edit or delete any saved queries, users with the Editor role can only edit or delete the queries they've saved. +Viewers can only reuse queries. +{{< /admonition >}} + #### Save a query To save a query you've created: diff --git a/eslint-suppressions.json b/eslint-suppressions.json index c5169c289b0..bc54f1c116e 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -397,7 +397,7 @@ }, "packages/grafana-prometheus/src/language_provider.ts": { "@typescript-eslint/no-explicit-any": { - "count": 7 + "count": 2 } }, "packages/grafana-prometheus/src/language_utils.ts": { @@ -786,11 +786,6 @@ "count": 13 } }, - "packages/grafana-ui/src/components/Sparkline/Sparkline.tsx": { - "react-prefer-function-component/react-prefer-function-component": { - "count": 1 - } - }, "packages/grafana-ui/src/components/Table/Cells/TableCell.tsx": { "@typescript-eslint/consistent-type-assertions": { "count": 3 diff --git a/packages/grafana-data/src/field/fieldDisplay.ts b/packages/grafana-data/src/field/fieldDisplay.ts index c4423173da3..3d82f571926 100644 --- a/packages/grafana-data/src/field/fieldDisplay.ts +++ b/packages/grafana-data/src/field/fieldDisplay.ts @@ -190,10 +190,62 @@ export const getFieldDisplayValues = (options: GetFieldDisplayValuesOptions): Fi y: dataFrame.fields[i], x: timeField, }; - if (calc === ReducerID.last) { - sparkline.highlightIndex = sparkline.y.values.length - 1; - } else if (calc === ReducerID.first) { - sparkline.highlightIndex = 0; + let highlightIdx: number | undefined = (() => { + switch (calc) { + case ReducerID.last: + return sparkline.y.values.length - 1; + case ReducerID.first: + return 0; + // TODO: #112977 enable more reducers for highlight index + // case ReducerID.lastNotNull: { + // for (let k = sparkline.y.values.length - 1; k >= 0; k--) { + // const v = sparkline.y.values[k]; + // if (v !== null && v !== undefined && !Number.isNaN(v)) { + // return k; + // } + // } + // return; + // } + // case ReducerID.firstNotNull: { + // for (let k = 0; k < sparkline.y.values.length; k++) { + // const v = sparkline.y.values[k]; + // if (v !== null && v !== undefined && !Number.isNaN(v)) { + // return k; + // } + // } + // return; + // } + // case ReducerID.min: { + // let minIdx = -1; + // let prevMin = Infinity; + // for (let k = 0; k < sparkline.y.values.length; k++) { + // const v = sparkline.y.values[k]; + // if (v !== null && v !== undefined && !Number.isNaN(v) && v < prevMin) { + // prevMin = v; + // minIdx = k; + // } + // } + // return minIdx >= 0 ? minIdx : undefined; + // } + // case ReducerID.max: { + // let maxIdx = -1; + // let prevMax = -Infinity; + // for (let k = 0; k < sparkline.y.values.length; k++) { + // const v = sparkline.y.values[k]; + // if (v !== null && v !== undefined && !Number.isNaN(v) && v > prevMax) { + // prevMax = v; + // maxIdx = k; + // } + // } + // return maxIdx >= 0 ? maxIdx : undefined; + // } + default: + return; + } + })(); + + if (typeof highlightIdx === 'number') { + sparkline.highlightIndex = highlightIdx; } } diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 901fc6d8cc2..767da439543 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -253,7 +253,7 @@ describe('applyFieldOverrides', () => { ], }); - it('will apply field overrides to the fields within the frame', () => { + it('will apply default field overrides to the fields within the frame', () => { const f0 = createDataFrame({ name: 'A', fields: [ @@ -285,6 +285,41 @@ describe('applyFieldOverrides', () => { expect(withOverrides[0].fields[1].values[0].fields[1].state.range.max).toBe(30); }); + it('will apply targeted field overrides to the fields within the frame', () => { + const f0 = createDataFrame({ + name: 'A', + fields: [ + { + name: 'message', + type: FieldType.string, + values: ['foo'], + }, + { + name: 'frame', + type: FieldType.frame, + values: [f0Internal], + }, + ], + }); + const withOverrides = applyFieldOverrides({ + data: [f0], + fieldConfig: { + defaults: {}, + overrides: [ + { + matcher: { id: FieldMatcherID.byName, options: 'frame' }, + properties: [{ id: 'max', value: 30 }], + }, + ], + }, + replaceVariables: (value) => value, + theme: createTheme(), + fieldConfigRegistry: customFieldRegistry, + }); + + expect(withOverrides[0].fields[1].values[0].fields[1].config.max).toBe(30); + }); + it('will not crash when some of the nested frames are undefined', () => { const f0 = createDataFrame({ name: 'A', diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 2aac477cd2b..8b345036c64 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -1,4 +1,4 @@ -import { isNumber, set, unset, get, cloneDeep } from 'lodash'; +import { isNumber, set, unset, get, cloneDeep, defaultsDeep } from 'lodash'; import { createContext, useContext, useMemo, useRef } from 'react'; import { usePrevious } from 'react-use'; @@ -240,9 +240,14 @@ export function applyFieldOverrides(options: ApplyFieldOverrideOptions): DataFra ...options, // nested frames can be `undefined` in certain situations, like after `merge` transform due to padding the value array. // let's replace them with empty frames to avoid errors applying overrides - data: field.values.map( - (nestedFrame: DataFrame | undefined): DataFrame => nestedFrame ?? createDataFrame({ fields: [] }) - ), + data: field.values.map((nestedFrame: DataFrame | undefined): DataFrame => { + const result = nestedFrame ?? createDataFrame({ fields: [] }); + result.fields = result.fields.map((newField) => { + newField.config = defaultsDeep(newField.config || {}, config); + return newField; + }); + return result; + }), }); } } @@ -256,7 +261,7 @@ function calculateRange( field: Field, globalRange: NumericRange | undefined, data: DataFrame[] -): { range?: { min?: number | null; max?: number | null; delta: number }; newGlobalRange: NumericRange | undefined } { +): { range?: NumericRange; newGlobalRange?: NumericRange } { // If range is defined with min/max, use it if (isNumber(config.min) && isNumber(config.max)) { const range = { min: config.min, max: config.max, delta: config.max - config.min }; diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index a0d2df1e542..4aeb394b81f 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -988,6 +988,11 @@ export interface FeatureToggles { */ restoreDashboards?: boolean; /** + * Enables recently viewed dashboards section in the browsing dashboard page + * @default false + */ + recentlyViewedDashboards?: boolean; + /** * Enable configuration of alert enrichments in Grafana Cloud. * @default false */ diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index b49be0d5363..34e672b66f5 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -288,6 +288,7 @@ export const availableIconsIndex = { bitbucket: true, git: true, 'tachometer-fast': true, + 'tachometer-empty': true, 'cmab-logo': true, }; diff --git a/packages/grafana-prometheus/src/constants.ts b/packages/grafana-prometheus/src/constants.ts index 24bc613de60..053180f4a13 100644 --- a/packages/grafana-prometheus/src/constants.ts +++ b/packages/grafana-prometheus/src/constants.ts @@ -1,8 +1,4 @@ // Max number of items (metrics, labels, values) that we display as suggestions. Prevents from running out of memory. -/** - * @deprecated - */ -export const SUGGESTIONS_LIMIT = 10000; export const PROMETHEUS_QUERY_BUILDER_MAX_RESULTS = 1000; @@ -45,8 +41,3 @@ export const GET_AND_POST_METADATA_ENDPOINTS = [ 'api/v1/labels', 'suggestions', ]; - -/** - * @deprecated - */ -export const REMOVE_SERIES_LIMIT = 'none'; diff --git a/packages/grafana-prometheus/src/index.ts b/packages/grafana-prometheus/src/index.ts index d0539d46591..f6950cc9fb7 100644 --- a/packages/grafana-prometheus/src/index.ts +++ b/packages/grafana-prometheus/src/index.ts @@ -86,7 +86,6 @@ export { PrometheusVariableSupport } from './variables'; export type { PrometheusLanguageProviderInterface } from './language_provider'; // For Metrics Drilldown -export { default as PromQlLanguageProvider } from './language_provider'; export { getPrometheusTime } from './language_utils'; export { isValidLegacyName, utf8Support, wrapUtf8Filters } from './utf8_support'; export { buildVisualQueryFromString } from './querybuilder/parsing'; diff --git a/packages/grafana-prometheus/src/language_provider.mock.ts b/packages/grafana-prometheus/src/language_provider.mock.ts index 09e16e5a9af..adf97211cb6 100644 --- a/packages/grafana-prometheus/src/language_provider.mock.ts +++ b/packages/grafana-prometheus/src/language_provider.mock.ts @@ -10,16 +10,6 @@ export class EmptyLanguageProviderMock { }); } - getLabelKeys = jest.fn().mockReturnValue([]); - getLabelValues = jest.fn().mockReturnValue([]); - getSeries = jest.fn().mockReturnValue({ __name__: [] }); - getSeriesValues = jest.fn().mockReturnValue([]); - fetchSeries = jest.fn().mockReturnValue([]); - fetchSeriesLabels = jest.fn().mockReturnValue([]); - fetchSeriesLabelsMatch = jest.fn().mockReturnValue([]); - fetchLabelsWithMatch = jest.fn().mockReturnValue([]); - fetchLabels = jest.fn(); - loadMetricsMetadata = jest.fn(); retrieveMetrics = jest.fn().mockReturnValue(['metric']); queryLabelKeys = jest.fn().mockResolvedValue([]); queryLabelValues = jest.fn().mockResolvedValue([]); diff --git a/packages/grafana-prometheus/src/language_provider.test.ts b/packages/grafana-prometheus/src/language_provider.test.ts index a3c32d7f881..6ddb6188117 100644 --- a/packages/grafana-prometheus/src/language_provider.test.ts +++ b/packages/grafana-prometheus/src/language_provider.test.ts @@ -1,26 +1,24 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/language_provider.test.ts import { AbstractLabelOperator, dateTime, TimeRange } from '@grafana/data'; +import { getCacheDurationInMinutes } from './caching'; +import { DEFAULT_SERIES_LIMIT } from './constants'; +import { PrometheusDatasource } from './datasource'; +import { + exportToAbstractQuery, + importFromAbstractQuery, + populateMatchParamsFromQueries, + PrometheusLanguageProvider, +} from './language_provider'; +import { getPrometheusTime, getRangeSnapInterval } from './language_utils'; +import { PrometheusCacheLevel, PromQuery } from './types'; + jest.mock('./language_utils', () => ({ ...jest.requireActual('./language_utils'), getPrometheusTime: jest.requireActual('./language_utils').getPrometheusTime, getRangeSnapInterval: jest.requireActual('./language_utils').getRangeSnapInterval, })); -import { getCacheDurationInMinutes } from './caching'; -import { Label } from './components/monaco-query-field/monaco-completion-provider/situation'; -import { DEFAULT_SERIES_LIMIT } from './constants'; -import { PrometheusDatasource } from './datasource'; -import { - exportToAbstractQuery, - importFromAbstractQuery, - PrometheusLanguageProviderInterface, - PrometheusLanguageProvider, - populateMatchParamsFromQueries, -} from './language_provider'; -import { getPrometheusTime, getRangeSnapInterval } from './language_utils'; -import { PrometheusCacheLevel, PromQuery } from './types'; - const now = new Date(1681300293392).getTime(); const timeRangeDurationSeconds = 1; const toPrometheusTime = getPrometheusTime(dateTime(now), false); @@ -58,607 +56,7 @@ const getMockQuantizedTimeRangeParams = (override?: Partial): TimeRan ...override, }); -// Common test helper to verify request parameters -const verifyRequestParams = ( - requestSpy: jest.SpyInstance, - expectedUrl: string, - expectedParams: unknown, - expectedOptions?: unknown -) => { - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy).toHaveBeenCalledWith(expectedUrl, expect.objectContaining(expectedParams), expectedOptions); -}; - -describe('Prometheus Language Provider', () => { - const defaultDatasource: PrometheusDatasource = { - metadataRequest: () => ({ data: { data: [] } }), - getTimeRangeParams: getTimeRangeParams, - interpolateString: (string: string) => string, - hasLabelsMatchAPISupport: () => false, - getDaysToCacheMetadata: () => 1, - getAdjustedInterval: () => getRangeSnapInterval(PrometheusCacheLevel.None, getMockQuantizedTimeRangeParams()), - cacheLevel: PrometheusCacheLevel.None, - getIntervalVars: () => ({}), - getRangeScopedVars: () => ({}), - seriesLimit: DEFAULT_SERIES_LIMIT, - } as unknown as PrometheusDatasource; - - describe('Series and label fetching', () => { - const timeRange = getMockTimeRange(); - - describe('getSeries', () => { - it('should use fetchDefaultSeries for empty selector', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - const fetchDefaultSeriesSpy = jest.spyOn(languageProvider, 'fetchDefaultSeries'); - fetchDefaultSeriesSpy.mockResolvedValue({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] }); - - const result = await languageProvider.getSeries(timeRange, '{}'); - - expect(fetchDefaultSeriesSpy).toHaveBeenCalledWith(timeRange); - expect(result).toEqual({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] }); - }); - - it('should use fetchSeriesLabels for non-empty selector', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - const fetchSeriesLabelsSpy = jest.spyOn(languageProvider, 'fetchSeriesLabels'); - fetchSeriesLabelsSpy.mockResolvedValue({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] }); - - const result = await languageProvider.getSeries(timeRange, '{job="grafana"}'); - - expect(fetchSeriesLabelsSpy).toHaveBeenCalledWith(timeRange, '{job="grafana"}', undefined, 'none'); - expect(result).toEqual({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] }); - }); - - it('should include name label when withName is true', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - const fetchSeriesLabelsSpy = jest.spyOn(languageProvider, 'fetchSeriesLabels'); - fetchSeriesLabelsSpy.mockResolvedValue({ __name__: ['metric1', 'metric2'], job: ['job1'] }); - - const result = await languageProvider.getSeries(timeRange, '{job="grafana"}', true); - - expect(fetchSeriesLabelsSpy).toHaveBeenCalledWith(timeRange, '{job="grafana"}', true, 'none'); - expect(result).toHaveProperty('__name__'); - expect(result.__name__).toEqual(['metric1', 'metric2']); - }); - - it('should handle errors gracefully', async () => { - jest.spyOn(console, 'error').mockImplementation(); - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - jest.spyOn(languageProvider, 'fetchSeriesLabels').mockRejectedValue(new Error('Network error')); - - const result = await languageProvider.getSeries(timeRange, '{job="grafana"}'); - - expect(result).toEqual({}); - }); - }); - - describe('getSeriesLabels', () => { - it('should call labels endpoint when API support is available', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - hasLabelsMatchAPISupport: () => true, - } as PrometheusDatasource); - const getSeriesLabels = languageProvider.getSeriesLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - const labelName = 'job'; - const labelValue = 'grafana'; - getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [ - { - name: labelName, - value: labelValue, - op: '=', - }, - ] as Label[]); - - verifyRequestParams(requestSpy, '/api/v1/labels', { - end: toPrometheusTimeString, - 'match[]': '{job="grafana"}', - start: fromPrometheusTimeString, - }); - }); - - it('should call series endpoint when API support is not available', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - getAdjustedInterval: (_: TimeRange) => - getRangeSnapInterval(PrometheusCacheLevel.None, getMockQuantizedTimeRangeParams()), - } as PrometheusDatasource); - const getSeriesLabels = languageProvider.getSeriesLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - const labelName = 'job'; - const labelValue = 'grafana'; - getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [ - { - name: labelName, - value: labelValue, - op: '=', - }, - ] as Label[]); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': '{job="grafana"}', - start: fromPrometheusTimeString, - }); - }); - - it('should call labels endpoint with quantized time parameters when cache level is set', () => { - const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Low); - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - hasLabelsMatchAPISupport: () => true, - cacheLevel: PrometheusCacheLevel.Low, - getAdjustedInterval: (_: TimeRange) => - getRangeSnapInterval(PrometheusCacheLevel.Low, getMockQuantizedTimeRangeParams()), - } as PrometheusDatasource); - const getSeriesLabels = languageProvider.getSeriesLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - const labelName = 'job'; - const labelValue = 'grafana'; - getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [ - { - name: labelName, - value: labelValue, - op: '=', - }, - ] as Label[]); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy).toHaveBeenCalledWith( - '/api/v1/labels', - { - end: ( - dateTime(fromPrometheusTime * 1000) - .add(timeSnapMinutes, 'minute') - .startOf('minute') - .valueOf() / 1000 - ).toString(), - 'match[]': '{job="grafana"}', - start: ( - dateTime(toPrometheusTime * 1000) - .startOf('minute') - .valueOf() / 1000 - ).toString(), - }, - { headers: { 'X-Grafana-Cache': `private, max-age=${timeSnapMinutes * 60}` } } - ); - }); - }); - - describe('getSeriesValues', () => { - it('should call series endpoint when labels match API is not supported', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - } as PrometheusDatasource); - const getSeriesValues = languageProvider.getSeriesValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - getSeriesValues(timeRange, 'job', '{job="grafana"}'); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': '{job="grafana"}', - start: fromPrometheusTimeString, - }); - }); - - it('should call label values endpoint when labels match API is supported', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - hasLabelsMatchAPISupport: () => true, - } as PrometheusDatasource); - const getSeriesValues = languageProvider.getSeriesValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - const labelName = 'job'; - const labelValue = 'grafana'; - getSeriesValues(timeRange, labelName, `{${labelName}="${labelValue}"}`); - - verifyRequestParams(requestSpy, `/api/v1/label/${labelName}/values`, { - end: toPrometheusTimeString, - 'match[]': `{${labelName}="${labelValue}"}`, - start: fromPrometheusTimeString, - }); - }); - - it('should properly interpolate template variables in queries', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'interpolated-'), - } as PrometheusDatasource); - const getSeriesValues = languageProvider.getSeriesValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - getSeriesValues(timeRange, 'job', '{instance="$instance", job="grafana"}'); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': '{instance="interpolated-instance", job="grafana"}', - start: fromPrometheusTimeString, - }); - }); - }); - - describe('fetchSeries', () => { - it('should use match[] parameter in request', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - await languageProvider.start(timeRange); - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await languageProvider.fetchSeries(timeRange, '{job="grafana"}'); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': '{job="grafana"}', - start: fromPrometheusTimeString, - }); - }); - }); - - describe('fetchSeriesLabels', () => { - it('should interpolate variables in series queries', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'interpolated-'), - } as PrometheusDatasource); - const fetchSeriesLabels = languageProvider.fetchSeriesLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchSeriesLabels(getMockTimeRange(), '$metric', undefined, DEFAULT_SERIES_LIMIT); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': 'interpolated-metric', - start: fromPrometheusTimeString, - limit: DEFAULT_SERIES_LIMIT, - }); - }); - - it('should not include limit parameter when "none" is specified', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - } as PrometheusDatasource); - const fetchSeriesLabels = languageProvider.fetchSeriesLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchSeriesLabels(getMockTimeRange(), 'metric-with-limit', undefined, 'none'); - - verifyRequestParams(requestSpy, '/api/v1/series', { - end: toPrometheusTimeString, - 'match[]': 'metric-with-limit', - start: fromPrometheusTimeString, - }); - }); - }); - }); - - describe('fetchLabels API', () => { - const tr = getMockTimeRange(); - - const getParams = (requestSpy: ReturnType) => { - return requestSpy.mock.calls[0][1]?.toString() ?? 'undefined'; - }; - - describe('with POST method', () => { - let languageProvider: PrometheusLanguageProviderInterface; - - beforeEach(() => { - languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - httpMethod: 'POST', - } as PrometheusDatasource); - }); - - it('should send single metric to request', async () => { - const mockQueries: PromQuery[] = [{ refId: 'C', expr: 'go_gc_pauses_seconds_bucket' }]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - const params = getParams(requestSpy); - expect(params).toMatch(encodeURI('match[]=go_gc_pauses_seconds_bucket')); - }); - - it('should extract metrics from complex PromQL expressions', async () => { - const mockQueries: PromQuery[] = [ - { - refId: 'C', - expr: 'histogram_quantile(0.95, sum(rate(go_gc_pauses_seconds_bucket[$__rate_interval])) by (le))', - }, - ]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - const params = getParams(requestSpy); - expect(params).toMatch(encodeURI('match[]=go_gc_pauses_seconds_bucket')); - }); - - it('should combine metrics from multiple queries', async () => { - const mockQueries: PromQuery[] = [ - { refId: 'B', expr: 'process_cpu_seconds_total' }, - { refId: 'C', expr: 'go_gc_pauses_seconds_bucket' }, - ]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - const params = getParams(requestSpy); - expect(params).toMatch(encodeURI('match[]=process_cpu_seconds_total&match[]=go_gc_pauses_seconds_bucket')); - }); - - it('should extract multiple metrics from binary operations', async () => { - const mockQueries: PromQuery[] = [ - { refId: 'B', expr: 'process_cpu_seconds_total + go_gc_pauses_seconds_bucket' }, - ]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - const params = getParams(requestSpy); - expect(params).toMatch(encodeURI('match[]=process_cpu_seconds_total&match[]=go_gc_pauses_seconds_bucket')); - }); - - it('should set and return labelKeys from API response', async () => { - const mockQueries: PromQuery[] = [{ refId: 'C', expr: 'go_gc_pauses_seconds_bucket' }]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['foo', 'bar']); - - const keys = await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - expect(languageProvider.labelKeys).toEqual(['bar', 'foo']); // Sorted order - expect(keys).toEqual(['bar', 'foo']); - }); - }); - - describe('with GET method', () => { - let languageProvider: PrometheusLanguageProviderInterface; - - beforeEach(() => { - languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - httpMethod: 'GET', - } as PrometheusDatasource); - }); - - it('should send query metrics in URL for GET requests', async () => { - const mockQueries: PromQuery[] = [{ refId: 'C', expr: 'go_gc_pauses_seconds_bucket' }]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy.mock.calls[0][0]).toMatch(encodeURI('match[]=go_gc_pauses_seconds_bucket')); - }); - - it('should handle empty queries correctly', async () => { - const mockQueries: PromQuery[] = [{ refId: 'A', expr: '' }]; - const fetchLabel = languageProvider.fetchLabels; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - await fetchLabel(tr, mockQueries); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy.mock.calls[0][0].indexOf('match[]')).toEqual(-1); - }); - }); - }); - - describe('Label value handling', () => { - describe('fetchLabelValues', () => { - it('should interpolate variables in labels', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'interpolated_'), - } as PrometheusDatasource); - const fetchLabelValues = languageProvider.fetchLabelValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchLabelValues(getMockTimeRange(), '$job'); - - verifyRequestParams(requestSpy, '/api/v1/label/interpolated_job/values', { - end: toPrometheusTimeString, - start: fromPrometheusTimeString, - }); - }); - - it('should properly encode UTF-8 labels', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'), - } as PrometheusDatasource); - const fetchLabelValues = languageProvider.fetchLabelValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchLabelValues(getMockTimeRange(), '"http.status:sum"'); - - verifyRequestParams(requestSpy, '/api/v1/label/U__http_2e_status:sum/values', { - end: toPrometheusTimeString, - start: fromPrometheusTimeString, - }); - }); - - it('should handle special characters safely in label values', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'value with spaces & special chars'), - } as PrometheusDatasource); - const fetchLabelValues = languageProvider.fetchLabelValues; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchLabelValues(getMockTimeRange(), '$job'); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy.mock.calls[0][0]).not.toContain(' '); - expect(requestSpy.mock.calls[0][0]).not.toContain('&'); - }); - }); - - describe('fetchSeriesValuesWithMatch', () => { - it('should handle UTF-8 encoding for special label names', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'), - } as PrometheusDatasource); - const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchSeriesValuesWithMatch(getMockTimeRange(), '"http.status:sum"', '{__name__="a_utf8_http_requests_total"}'); - - verifyRequestParams(requestSpy, '/api/v1/label/U__http_2e_status:sum/values', { - end: toPrometheusTimeString, - start: fromPrometheusTimeString, - 'match[]': '{__name__="a_utf8_http_requests_total"}', - }); - }); - - it('should not encode standard Prometheus label names', () => { - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - } as PrometheusDatasource); - const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch; - const requestSpy = jest.spyOn(languageProvider, 'request'); - - fetchSeriesValuesWithMatch(getMockTimeRange(), '"http_status_sum"', '{__name__="a_utf8_http_requests_total"}'); - - verifyRequestParams(requestSpy, '/api/v1/label/http_status_sum/values', { - end: toPrometheusTimeString, - start: fromPrometheusTimeString, - 'match[]': '{__name__="a_utf8_http_requests_total"}', - }); - }); - }); - }); - - describe('fetchSuggestions', () => { - it('should send POST request with correct parameters', async () => { - const timeRange = getMockTimeRange(); - const mockQueries: PromQuery[] = [{ refId: 'A', expr: 'metric1' }]; - - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - interpolateString: (string: string) => `interpolated_${string}`, - getIntervalVars: () => ({ __interval: '1m' }), - getRangeScopedVars: () => ({ __range: { text: '1h', value: '1h' } }), - } as unknown as PrometheusDatasource); - - const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['suggestion1', 'suggestion2']); - - // Simplifying the test by not passing complex scope objects that require more type definitions - const result = await languageProvider.fetchSuggestions( - timeRange, - mockQueries, - undefined, // omitting scopes parameter - [{ key: 'instance', operator: '=', value: 'localhost' }], - 'metric', - 100 - ); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy.mock.calls[0][0]).toBe('/suggestions'); - - // Check method and content type - expect(requestSpy.mock.calls[0][2]).toMatchObject({ - headers: { 'Content-Type': 'application/json' }, - method: 'POST', - }); - - // Check query parameters - expect(requestSpy.mock.calls[0][1]).toMatchObject({ - labelName: 'metric', - limit: 100, - queries: ['interpolated_metric1'], - }); - - expect(result).toEqual(['suggestion1', 'suggestion2']); - }); - - it('should use default time range if not provided', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['result']); - - await languageProvider.fetchSuggestions(undefined, [], [], [], 'test'); - - expect(requestSpy).toHaveBeenCalled(); - // Default time range should be used - expect(requestSpy.mock.calls[0][1]).toHaveProperty('start'); - expect(requestSpy.mock.calls[0][1]).toHaveProperty('end'); - }); - - it('should handle empty response gracefully', async () => { - const languageProvider = new PrometheusLanguageProvider(defaultDatasource); - jest.spyOn(languageProvider, 'request').mockResolvedValue(null); - - const result = await languageProvider.fetchSuggestions(getMockTimeRange(), [], [], [], 'test'); - - expect(result).toEqual([]); - }); - - it('should include cache headers when cacheLevel is set', async () => { - const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Medium); - const languageProvider = new PrometheusLanguageProvider({ - ...defaultDatasource, - cacheLevel: PrometheusCacheLevel.Medium, - } as PrometheusDatasource); - - const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['result']); - - await languageProvider.fetchSuggestions(getMockTimeRange(), [], [], [], 'test'); - - expect(requestSpy).toHaveBeenCalled(); - expect(requestSpy.mock.calls[0][2]?.headers).toHaveProperty('X-Grafana-Cache'); - expect(requestSpy.mock.calls[0][2]?.headers?.['X-Grafana-Cache']).toContain( - `private, max-age=${timeSnapMinutes * 60}` - ); - }); - }); -}); - -describe('Query transformation', () => { - describe('importFromAbstractQuery', () => { - it('should handle empty queries', async () => { - const result = importFromAbstractQuery({ refId: 'bar', labelMatchers: [] }); - expect(result).toEqual({ refId: 'bar', expr: '', range: true }); - }); - }); - - describe('exportToAbstractQuery', () => { - it('should extract labels and metric name from PromQL', async () => { - const abstractQuery = exportToAbstractQuery({ - refId: 'bar', - expr: 'metric_name{label1="value1", label2!="value2", label3=~"value3", label4!~"value4"}', - instant: true, - range: false, - }); - - expect(abstractQuery).toMatchObject({ - refId: 'bar', - labelMatchers: [ - { name: 'label1', operator: AbstractLabelOperator.Equal, value: 'value1' }, - { name: 'label2', operator: AbstractLabelOperator.NotEqual, value: 'value2' }, - { name: 'label3', operator: AbstractLabelOperator.EqualRegEx, value: 'value3' }, - { name: 'label4', operator: AbstractLabelOperator.NotEqualRegEx, value: 'value4' }, - { name: '__name__', operator: AbstractLabelOperator.Equal, value: 'metric_name' }, - ], - }); - }); - }); -}); - -describe('PrometheusLanguageProvider with feature toggle', () => { +describe('PrometheusLanguageProvider', () => { const defaultDatasource: PrometheusDatasource = { metadataRequest: () => ({ data: { data: [] } }), getTimeRangeParams: getTimeRangeParams, @@ -712,7 +110,6 @@ describe('PrometheusLanguageProvider with feature toggle', () => { expect(resourceClientStartSpy).toHaveBeenCalled(); expect(queryMetadataSpy).toHaveBeenCalled(); expect(provider.retrieveMetricsMetadata()).toEqual(mockMetadata); - expect(provider.metricsMetadata).toEqual(mockMetadata); // Check backward compatibility }); it('should call queryMetricsMetadata with datasource seriesLimit during start', async () => { @@ -755,6 +152,7 @@ describe('PrometheusLanguageProvider with feature toggle', () => { const customLimit = 1000; const queryMetadataSpy = jest.spyOn(provider as any, '_queryMetadata').mockResolvedValue(mockMetadata); + // TODO spy on /api/v1/metadata endpoint or fetch const result = await provider.queryMetricsMetadata(customLimit); expect(queryMetadataSpy).toHaveBeenCalledWith(customLimit); @@ -1034,4 +432,118 @@ describe('PrometheusLanguageProvider with feature toggle', () => { expect(result).toEqual(['__name__=~"go_cpu_classes_idle_cpu_seconds_total"']); }); }); + + describe('fetchSuggestions', () => { + it('should send POST request with correct parameters', async () => { + const timeRange = getMockTimeRange(); + const mockQueries: PromQuery[] = [{ refId: 'A', expr: 'metric1' }]; + + const languageProvider = new PrometheusLanguageProvider({ + ...defaultDatasource, + interpolateString: (string: string) => `interpolated_${string}`, + getIntervalVars: () => ({ __interval: '1m' }), + getRangeScopedVars: () => ({ __range: { text: '1h', value: '1h' } }), + } as unknown as PrometheusDatasource); + + const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['suggestion1', 'suggestion2']); + + // Simplifying the test by not passing complex scope objects that require more type definitions + const result = await languageProvider.fetchSuggestions( + timeRange, + mockQueries, + undefined, // omitting scopes parameter + [{ key: 'instance', operator: '=', value: 'localhost' }], + 'metric', + 100 + ); + + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0][0]).toBe('/suggestions'); + + // Check method and content type + expect(requestSpy.mock.calls[0][2]).toMatchObject({ + headers: { 'Content-Type': 'application/json' }, + method: 'POST', + }); + + // Check query parameters + expect(requestSpy.mock.calls[0][1]).toMatchObject({ + labelName: 'metric', + limit: 100, + queries: ['interpolated_metric1'], + }); + + expect(result).toEqual(['suggestion1', 'suggestion2']); + }); + + it('should use default time range if not provided', async () => { + const languageProvider = new PrometheusLanguageProvider(defaultDatasource); + const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['result']); + + await languageProvider.fetchSuggestions(undefined, [], [], [], 'test'); + + expect(requestSpy).toHaveBeenCalled(); + // Default time range should be used + expect(requestSpy.mock.calls[0][1]).toHaveProperty('start'); + expect(requestSpy.mock.calls[0][1]).toHaveProperty('end'); + }); + + it('should handle empty response gracefully', async () => { + const languageProvider = new PrometheusLanguageProvider(defaultDatasource); + jest.spyOn(languageProvider, 'request').mockResolvedValue(null); + + const result = await languageProvider.fetchSuggestions(getMockTimeRange(), [], [], [], 'test'); + + expect(result).toEqual([]); + }); + + it('should include cache headers when cacheLevel is set', async () => { + const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Medium); + const languageProvider = new PrometheusLanguageProvider({ + ...defaultDatasource, + cacheLevel: PrometheusCacheLevel.Medium, + } as PrometheusDatasource); + + const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['result']); + + await languageProvider.fetchSuggestions(getMockTimeRange(), [], [], [], 'test'); + + expect(requestSpy).toHaveBeenCalled(); + expect(requestSpy.mock.calls[0][2]?.headers).toHaveProperty('X-Grafana-Cache'); + expect(requestSpy.mock.calls[0][2]?.headers?.['X-Grafana-Cache']).toContain( + `private, max-age=${timeSnapMinutes * 60}` + ); + }); + }); +}); + +describe('Query transformation', () => { + describe('importFromAbstractQuery', () => { + it('should handle empty queries', async () => { + const result = importFromAbstractQuery({ refId: 'bar', labelMatchers: [] }); + expect(result).toEqual({ refId: 'bar', expr: '', range: true }); + }); + }); + + describe('exportToAbstractQuery', () => { + it('should extract labels and metric name from PromQL', async () => { + const abstractQuery = exportToAbstractQuery({ + refId: 'bar', + expr: 'metric_name{label1="value1", label2!="value2", label3=~"value3", label4!~"value4"}', + instant: true, + range: false, + }); + + expect(abstractQuery).toMatchObject({ + refId: 'bar', + labelMatchers: [ + { name: 'label1', operator: AbstractLabelOperator.Equal, value: 'value1' }, + { name: 'label2', operator: AbstractLabelOperator.NotEqual, value: 'value2' }, + { name: 'label3', operator: AbstractLabelOperator.EqualRegEx, value: 'value3' }, + { name: 'label4', operator: AbstractLabelOperator.NotEqualRegEx, value: 'value4' }, + { name: '__name__', operator: AbstractLabelOperator.Equal, value: 'metric_name' }, + ], + }); + }); + }); }); diff --git a/packages/grafana-prometheus/src/language_provider.ts b/packages/grafana-prometheus/src/language_provider.ts index 568acddba64..0daf12d7b7e 100644 --- a/packages/grafana-prometheus/src/language_provider.ts +++ b/packages/grafana-prometheus/src/language_provider.ts @@ -1,5 +1,4 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/language_provider.ts -import { once } from 'lodash'; import Prism from 'prismjs'; import { @@ -8,7 +7,6 @@ import { AbstractQuery, AdHocVariableFilter, getDefaultTimeRange, - LanguageProvider, Scope, scopeFilterOperatorMap, ScopeSpecFilter, @@ -17,34 +15,12 @@ import { import { BackendSrvRequest } from '@grafana/runtime'; import { buildCacheHeaders, getDaysToCacheMetadata, getDefaultCacheHeaders } from './caching'; -import { Label } from './components/monaco-query-field/monaco-completion-provider/situation'; -import { DEFAULT_SERIES_LIMIT, EMPTY_SELECTOR, REMOVE_SERIES_LIMIT } from './constants'; import { PrometheusDatasource } from './datasource'; -import { - extractLabelMatchers, - fixSummariesMetadata, - processHistogramMetrics, - processLabels, - removeQuotesIfExist, - toPromLikeQuery, -} from './language_utils'; +import { extractLabelMatchers, fixSummariesMetadata, toPromLikeQuery } from './language_utils'; import { promqlGrammar } from './promql'; import { buildVisualQueryFromString } from './querybuilder/parsing'; import { LabelsApiClient, ResourceApiClient, SeriesApiClient } from './resource_clients'; import { PromMetricsMetadata, PromQuery } from './types'; -import { escapeForUtf8Support, isValidLegacyName } from './utf8_support'; - -const DEFAULT_KEYS = ['job', 'instance']; - -/** - * Prometheus API endpoints for fetching resources - */ -const API_V1 = { - METADATA: '/api/v1/metadata', - SERIES: '/api/v1/series', - LABELS: '/api/v1/labels', - LABELS_VALUES: (labelKey: string) => `/api/v1/label/${labelKey}/values`, -}; interface PrometheusBaseLanguageProvider { datasource: PrometheusDatasource; @@ -53,7 +29,7 @@ interface PrometheusBaseLanguageProvider { * When no timeRange provided, we will use the default time range (now/now-6h) * @param timeRange */ - start: (timeRange?: TimeRange) => Promise; + start: (timeRange?: TimeRange) => Promise; request: (url: string, params?: any, options?: Partial) => Promise; @@ -68,455 +44,6 @@ interface PrometheusBaseLanguageProvider { ) => Promise; } -/** - * @deprecated This interface is deprecated and will be removed. - */ -interface PrometheusLegacyLanguageProvider { - /** - * @deprecated Use retrieveHistogramMetrics() method instead - */ - histogramMetrics: string[]; - /** - * @deprecated Use retrieveMetrics() method instead - */ - metrics: string[]; - /** - * @deprecated Use retrieveMetricsMetadata() method instead - */ - metricsMetadata?: PromMetricsMetadata; - /** - * @deprecated Use retrieveLabelKeys() method instead - */ - labelKeys: string[]; - - /** - * @deprecated Use queryMetricsMetadata() method instead. - */ - loadMetricsMetadata: () => void; - /** - * @deprecated Use retrieveMetricsMetadata() method instead - */ - getLabelKeys: () => string[]; - /** - * @deprecated If you need labelKeys or labelValues please use queryLabelKeys() or queryLabelValues() functions - */ - getSeries: (timeRange: TimeRange, selector: string, withName?: boolean) => Promise>; - /** - * @deprecated Use queryLabelValues() method insteadIt'll determine the right endpoint based on the datasource settings - */ - fetchLabelValues: (range: TimeRange, key: string, limit?: string | number) => Promise; - /** - * @deprecated Use queryLabelValues() method insteadIt'll determine the right endpoint based on the datasource settings - */ - getLabelValues: (range: TimeRange, key: string) => Promise; - /** - * @deprecated If you need labelKeys or labelValues please use queryLabelKeys() or queryLabelValues() functions - */ - fetchLabels: (timeRange: TimeRange, queries?: PromQuery[], limit?: string) => Promise; - /** - * @deprecated Use queryLabelValues() method insteadIt'll determine the right endpoint based on the datasource settings - */ - getSeriesValues: (timeRange: TimeRange, labelName: string, selector: string) => Promise; - /** - * @deprecated Use queryLabelValues() method insteadIt'll determine the right endpoint based on the datasource settings - */ - fetchSeriesValuesWithMatch: ( - timeRange: TimeRange, - name: string, - match?: string, - requestId?: string, - withLimit?: string | number - ) => Promise; - /** - * @deprecated Use queryLabelKeys() method instead. It'll determine the right endpoint based on the datasource settings - */ - getSeriesLabels: (timeRange: TimeRange, selector: string, otherLabels: Label[]) => Promise; - /** - * @deprecated Use queryLabelKeys() method instead. It'll determine the right endpoint based on the datasource settings - */ - fetchLabelsWithMatch: ( - timeRange: TimeRange, - name: string, - withName?: boolean, - withLimit?: string | number - ) => Promise>; - /** - * @deprecated Use queryLabelKeys() method instead. It'll determine the right endpoint based on the datasource settings - */ - fetchSeriesLabels: ( - timeRange: TimeRange, - name: string, - withName?: boolean, - withLimit?: string | number - ) => Promise>; - /** - * @deprecated Use queryLabelKeys() method instead. It'll determine the right endpoint based on the datasource settings - */ - fetchSeriesLabelsMatch: ( - timeRange: TimeRange, - name: string, - withLimit?: string | number - ) => Promise>; - /** - * @deprecated If you need labelKeys or labelValues please use queryLabelKeys() or queryLabelValues() functions - */ - fetchSeries: (timeRange: TimeRange, match: string) => Promise>>; - /** - * @deprecated If you need labelKeys or labelValues please use queryLabelKeys() or queryLabelValues() functions - */ - fetchDefaultSeries: (timeRange: TimeRange) => Promise<{}>; -} - -/** - * Old implementation of prometheus language provider. - * @deprecated Use PrometheusLanguageProviderInterface and PrometheusLanguageProvider class instead. - */ -export default class PromQlLanguageProvider extends LanguageProvider implements PrometheusLegacyLanguageProvider { - declare startTask: Promise; - declare labelFetchTs: number; - - datasource: PrometheusDatasource; - - histogramMetrics: string[]; - metrics: string[]; - metricsMetadata?: PromMetricsMetadata; - labelKeys: string[] = []; - - constructor(datasource: PrometheusDatasource, initialValues?: Partial) { - super(); - - this.datasource = datasource; - this.histogramMetrics = []; - this.metrics = []; - - Object.assign(this, initialValues); - } - - request = async (url: string, params = {}, options?: Partial) => { - try { - const res = await this.datasource.metadataRequest(url, params, options); - return res.data.data; - } catch (error) { - if (!isCancelledError(error)) { - console.error(error); - } - } - - return undefined; - }; - - /** - * Overridden by PrometheusLanguageProvider - */ - start = async (timeRange: TimeRange = getDefaultTimeRange()): Promise => { - if (this.datasource.lookupsDisabled) { - return []; - } - - this.metrics = (await this.fetchLabelValues(timeRange, '__name__')) || []; - this.histogramMetrics = processHistogramMetrics(this.metrics).sort(); - return Promise.all([this.loadMetricsMetadata(), this.fetchLabels(timeRange)]); - }; - - async loadMetricsMetadata() { - const secondsInDay = 86400; - const headers = buildCacheHeaders(getDaysToCacheMetadata(this.datasource.cacheLevel) * secondsInDay); - this.metricsMetadata = fixSummariesMetadata( - await this.request( - API_V1.METADATA, - {}, - { - showErrorAlert: false, - ...headers, - } - ) - ); - } - - getLabelKeys(): string[] { - return this.labelKeys; - } - - async getSeries(timeRange: TimeRange, selector: string, withName?: boolean): Promise> { - if (this.datasource.lookupsDisabled) { - return {}; - } - try { - if (selector === EMPTY_SELECTOR) { - return await this.fetchDefaultSeries(timeRange); - } else { - return await this.fetchSeriesLabels(timeRange, selector, withName, REMOVE_SERIES_LIMIT); - } - } catch (error) { - // TODO: better error handling - console.error(error); - return {}; - } - } - - fetchLabelValues = async (range: TimeRange, key: string, limit?: string | number): Promise => { - const params = { ...this.datasource.getAdjustedInterval(range), ...(limit ? { limit } : {}) }; - const interpolatedName = this.datasource.interpolateString(key); - const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName)); - const value = await this.request( - API_V1.LABELS_VALUES(interpolatedAndEscapedName), - params, - getDefaultCacheHeaders(this.datasource.cacheLevel) - ); - return value ?? []; - }; - - async getLabelValues(range: TimeRange, key: string): Promise { - return await this.fetchLabelValues(range, key); - } - - /** - * Fetches all label keys - */ - fetchLabels = async (timeRange: TimeRange, queries?: PromQuery[], limit?: string): Promise => { - let url = API_V1.LABELS; - const timeParams = this.datasource.getAdjustedInterval(timeRange); - this.labelFetchTs = Date.now().valueOf(); - - const searchParams = new URLSearchParams({ ...timeParams, ...(limit ? { limit } : {}) }); - queries?.forEach((q) => { - const visualQuery = buildVisualQueryFromString(q.expr); - if (visualQuery.query.metric !== '') { - const isUtf8Metric = !isValidLegacyName(visualQuery.query.metric); - searchParams.append('match[]', isUtf8Metric ? `{"${visualQuery.query.metric}"}` : visualQuery.query.metric); - if (visualQuery.query.binaryQueries) { - visualQuery.query.binaryQueries.forEach((bq) => { - searchParams.append('match[]', isUtf8Metric ? `{"${bq.query.metric}"}` : bq.query.metric); - }); - } - } - }); - - if (this.datasource.httpMethod === 'GET') { - url += `?${searchParams.toString()}`; - } - - const res = await this.request(url, searchParams, getDefaultCacheHeaders(this.datasource.cacheLevel)); - if (Array.isArray(res)) { - this.labelKeys = res.slice().sort(); - return [...this.labelKeys]; - } - - return []; - }; - - /** - * Gets series values - * Function to replace old getSeries calls in a way that will provide faster endpoints - * for new prometheus instances, while maintaining backward compatability - */ - getSeriesValues = async (timeRange: TimeRange, labelName: string, selector: string): Promise => { - if (!this.datasource.hasLabelsMatchAPISupport()) { - const data = await this.getSeries(timeRange, selector); - return data[removeQuotesIfExist(labelName)] ?? []; - } - return await this.fetchSeriesValuesWithMatch(timeRange, labelName, selector); - }; - - /** - * Fetches all values for a label, with optional match[] - */ - fetchSeriesValuesWithMatch = async ( - timeRange: TimeRange, - name: string, - match?: string, - requestId?: string, - withLimit?: string | number - ): Promise => { - const interpolatedName = name ? this.datasource.interpolateString(name) : null; - const interpolatedMatch = match ? this.datasource.interpolateString(match) : null; - const range = this.datasource.getAdjustedInterval(timeRange); - const urlParams = { - ...range, - ...(interpolatedMatch && { 'match[]': interpolatedMatch }), - ...(withLimit ? { limit: withLimit } : {}), - }; - let requestOptions: Partial | undefined = { - ...getDefaultCacheHeaders(this.datasource.cacheLevel), - ...(requestId && { requestId }), - }; - - if (!Object.keys(requestOptions).length) { - requestOptions = undefined; - } - - const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName ?? '')); - - const value = await this.request(API_V1.LABELS_VALUES(interpolatedAndEscapedName), urlParams, requestOptions); - return value ?? []; - }; - - /** - * Gets series labels - * Function to replace old getSeries calls in a way that will provide faster endpoints for new prometheus instances, - * while maintaining backward compatability. The old API call got the labels and the values in a single query, - * but with the new query we need two calls, one to get the labels, and another to get the values. - */ - getSeriesLabels = async (timeRange: TimeRange, selector: string, otherLabels: Label[]): Promise => { - let possibleLabelNames, data: Record; - - if (!this.datasource.hasLabelsMatchAPISupport()) { - data = await this.getSeries(timeRange, selector); - possibleLabelNames = Object.keys(data); // all names from prometheus - } else { - // Exclude __name__ from output - otherLabels.push({ name: '__name__', value: '', op: '!=' }); - data = await this.fetchSeriesLabelsMatch(timeRange, selector); - possibleLabelNames = Object.keys(data); - } - - const usedLabelNames = new Set(otherLabels.map((l) => l.name)); // names used in the query - return possibleLabelNames.filter((l) => !usedLabelNames.has(l)); - }; - - /** - * Fetch labels using the best endpoint that datasource supports. - * This is cached by its args but also by the global timeRange currently selected as they can change over requested time. - */ - fetchLabelsWithMatch = async ( - timeRange: TimeRange, - name: string, - withName?: boolean, - withLimit?: string | number - ): Promise> => { - if (this.datasource.hasLabelsMatchAPISupport()) { - return this.fetchSeriesLabelsMatch(timeRange, name, withLimit); - } else { - return this.fetchSeriesLabels(timeRange, name, withName, REMOVE_SERIES_LIMIT); - } - }; - - /** - * Fetch labels for a series using /series endpoint. This is cached by its args but also by the global timeRange currently selected as - * they can change over requested time. - */ - fetchSeriesLabels = async ( - timeRange: TimeRange, - name: string, - withName?: boolean, - withLimit?: string | number - ): Promise> => { - const interpolatedName = this.datasource.interpolateString(name); - const range = this.datasource.getAdjustedInterval(timeRange); - let urlParams = { - ...range, - 'match[]': interpolatedName, - ...(withLimit !== 'none' ? { limit: withLimit ?? DEFAULT_SERIES_LIMIT } : {}), - }; - - const data = await this.request(API_V1.SERIES, urlParams, getDefaultCacheHeaders(this.datasource.cacheLevel)); - const { values } = processLabels(data, withName); - return values; - }; - - /** - * Fetch labels for a series using /labels endpoint. This is cached by its args but also by the global timeRange currently selected as - * they can change over requested time. - */ - fetchSeriesLabelsMatch = async ( - timeRange: TimeRange, - name: string, - withLimit?: string | number - ): Promise> => { - const interpolatedName = this.datasource.interpolateString(name); - const range = this.datasource.getAdjustedInterval(timeRange); - const urlParams = { - ...range, - 'match[]': interpolatedName, - ...(withLimit ? { limit: withLimit } : {}), - }; - - const data: string[] = await this.request( - API_V1.LABELS, - urlParams, - getDefaultCacheHeaders(this.datasource.cacheLevel) - ); - // Convert string array to Record - return data.reduce((ac, a) => ({ ...ac, [a]: '' }), {}); - }; - - /** - * Fetch series for a selector. Use this for raw results. Use fetchSeriesLabels() to get labels. - */ - fetchSeries = async (timeRange: TimeRange, match: string): Promise>> => { - const range = this.datasource.getTimeRangeParams(timeRange); - const params = { ...range, 'match[]': match }; - return await this.request(API_V1.SERIES, params, getDefaultCacheHeaders(this.datasource.cacheLevel)); - }; - - /** - * Fetch this only one as we assume this won't change over time. This is cached differently from fetchSeriesLabels - * because we can cache more aggressively here and also we do not want to invalidate this cache the same way as in - * fetchSeriesLabels. - */ - fetchDefaultSeries = once(async (timeRange: TimeRange) => { - const values = await Promise.all(DEFAULT_KEYS.map((key) => this.fetchLabelValues(timeRange, key))); - return DEFAULT_KEYS.reduce((acc, key, i) => ({ ...acc, [key]: values[i] }), {}); - }); - - /** - * Fetch labels or values for a label based on the queries, scopes, filters and time range - */ - fetchSuggestions = async ( - timeRange?: TimeRange, - queries?: PromQuery[], - scopes?: Scope[], - adhocFilters?: AdHocVariableFilter[], - labelName?: string, - limit?: number, - requestId?: string - ): Promise => { - if (!timeRange) { - timeRange = getDefaultTimeRange(); - } - - const url = '/suggestions'; - const timeParams = this.datasource.getAdjustedInterval(timeRange); - const value = await this.request( - url, - { - labelName, - queries: queries?.map((q) => - this.datasource.interpolateString(q.expr, { - ...this.datasource.getIntervalVars(), - ...this.datasource.getRangeScopedVars(timeRange), - }) - ), - scopes: scopes?.reduce((acc, scope) => { - if (scope.spec.filters) { - acc.push(...scope.spec.filters); - } - - return acc; - }, []), - adhocFilters: adhocFilters?.map((filter) => ({ - key: filter.key, - operator: scopeFilterOperatorMap[filter.operator], - value: filter.value, - values: filter.values, - })), - limit, - ...timeParams, - }, - { - ...(requestId && { requestId }), - headers: { - ...getDefaultCacheHeaders(this.datasource.cacheLevel)?.headers, - 'Content-Type': 'application/json', - }, - method: 'POST', - } - ); - - return value ?? []; - }; -} - /** * Modern implementation of the Prometheus language provider that abstracts API endpoint selection. * @@ -531,9 +58,7 @@ export default class PromQlLanguageProvider extends LanguageProvider implements * @see LabelsApiClient For modern Prometheus versions using the labels API * @see SeriesApiClient For legacy Prometheus versions using the series API */ -export interface PrometheusLanguageProviderInterface - extends PrometheusBaseLanguageProvider, - PrometheusLegacyLanguageProvider { +export interface PrometheusLanguageProviderInterface extends PrometheusBaseLanguageProvider { /** * Initializes the language provider by fetching metrics, label keys, and metrics metadata using Resource Clients. * All calls use the limit parameter from datasource configuration (default: 40,000 if not set). @@ -541,7 +66,7 @@ export interface PrometheusLanguageProviderInterface * For backward compatibility, it calls _backwardCompatibleStart. * Some places still rely on deprecated fields. Until we replace them, we need _backwardCompatibleStart method. */ - start: (timeRange?: TimeRange) => Promise; + start: (timeRange?: TimeRange) => Promise; /** * Returns already cached metrics metadata including type and help information. @@ -591,14 +116,29 @@ export interface PrometheusLanguageProviderInterface queryLabelValues: (timeRange: TimeRange, labelKey: string, match?: string, limit?: number) => Promise; } -export class PrometheusLanguageProvider extends PromQlLanguageProvider implements PrometheusLanguageProviderInterface { +export class PrometheusLanguageProvider implements PrometheusLanguageProviderInterface { + public datasource: PrometheusDatasource; + private _metricsMetadata?: PromMetricsMetadata; private _resourceClient?: ResourceApiClient; constructor(datasource: PrometheusDatasource) { - super(datasource); + this.datasource = datasource; } + request = async (url: string, params = {}, options?: Partial) => { + try { + const res = await this.datasource.metadataRequest(url, params, options); + return res.data.data; + } catch (error) { + if (!isCancelledError(error)) { + console.error(error); + } + } + + return undefined; + }; + /** * Lazily initializes and returns the appropriate resource client based on Prometheus version. * @@ -624,24 +164,14 @@ export class PrometheusLanguageProvider extends PromQlLanguageProvider implement * Same start logic but it uses resource clients. Backward compatibility it calls _backwardCompatibleStart. * Some places still relies on deprecated fields. Until we replace them we need _backwardCompatibleStart method */ - start = async (timeRange: TimeRange = getDefaultTimeRange()): Promise => { + start = async (timeRange: TimeRange = getDefaultTimeRange()): Promise => { if (this.datasource.lookupsDisabled) { return []; } - await Promise.all([this.resourceClient.start(timeRange), this.queryMetricsMetadata(this.datasource.seriesLimit)]); - return this._backwardCompatibleStart(); - }; - - /** - * This private method exists to make sure the old class will be functional until we remove it. - * When we remove old class (PromQlLanguageProvider) we should remove this method too. - */ - private _backwardCompatibleStart = async () => { - this.metricsMetadata = this.retrieveMetricsMetadata(); - this.metrics = this.retrieveMetrics(); - this.histogramMetrics = this.retrieveHistogramMetrics(); - this.labelKeys = this.retrieveLabelKeys(); - return []; + return await Promise.all([ + this.resourceClient.start(timeRange), + this.queryMetricsMetadata(this.datasource.seriesLimit), + ]); }; /** @@ -654,7 +184,7 @@ export class PrometheusLanguageProvider extends PromQlLanguageProvider implement const secondsInDay = 86400; const headers = buildCacheHeaders(getDaysToCacheMetadata(this.datasource.cacheLevel) * secondsInDay); const metadata = await this.request( - API_V1.METADATA, + `/api/v1/metadata`, { limit: limit ?? this.datasource.seriesLimit }, { showErrorAlert: false, @@ -776,6 +306,63 @@ export class PrometheusLanguageProvider extends PromQlLanguageProvider implement limit ); }; + + /** + * Fetch labels or values for a label based on the queries, scopes, filters and time range + */ + fetchSuggestions = async ( + timeRange?: TimeRange, + queries?: PromQuery[], + scopes?: Scope[], + adhocFilters?: AdHocVariableFilter[], + labelName?: string, + limit?: number, + requestId?: string + ): Promise => { + if (!timeRange) { + timeRange = getDefaultTimeRange(); + } + + const url = '/suggestions'; + const timeParams = this.datasource.getAdjustedInterval(timeRange); + const value = await this.request( + url, + { + labelName, + queries: queries?.map((q) => + this.datasource.interpolateString(q.expr, { + ...this.datasource.getIntervalVars(), + ...this.datasource.getRangeScopedVars(timeRange), + }) + ), + scopes: scopes?.reduce((acc, scope) => { + if (scope.spec.filters) { + acc.push(...scope.spec.filters); + } + + return acc; + }, []), + adhocFilters: adhocFilters?.map((filter) => ({ + key: filter.key, + operator: scopeFilterOperatorMap[filter.operator], + value: filter.value, + values: filter.values, + })), + limit, + ...timeParams, + }, + { + ...(requestId && { requestId }), + headers: { + ...getDefaultCacheHeaders(this.datasource.cacheLevel)?.headers, + 'Content-Type': 'application/json', + }, + method: 'POST', + } + ); + + return value ?? []; + }; } export const importFromAbstractQuery = (labelBasedQuery: AbstractQuery): PromQuery => { diff --git a/packages/grafana-prometheus/src/language_utils.test.ts b/packages/grafana-prometheus/src/language_utils.test.ts index 555634f898e..6e69b7844d7 100644 --- a/packages/grafana-prometheus/src/language_utils.test.ts +++ b/packages/grafana-prometheus/src/language_utils.test.ts @@ -9,7 +9,6 @@ import { fixSummariesMetadata, getPrometheusTime, getRangeSnapInterval, - processLabels, removeQuotesIfExist, toPromLikeQuery, truncateResult, @@ -507,57 +506,6 @@ describe('truncateResult', () => { }); }); -describe('processLabels', () => { - it('export abstract query to expr', () => { - const labels: Array<{ [key: string]: string }> = [ - { label1: 'value1' }, - { label2: 'value2' }, - { label3: 'value3' }, - { label1: 'value1' }, - { label1: 'value1b' }, - ]; - - expect(processLabels(labels)).toEqual({ - keys: ['label1', 'label2', 'label3'], - values: { label1: ['value1', 'value1b'], label2: ['value2'], label3: ['value3'] }, - }); - }); - - it('dont wrap utf8 label values with quotes', () => { - const labels: Array<{ [key: string]: string }> = [ - { label1: 'value1' }, - { label2: 'value2' }, - { label3: 'value3 with space' }, - { label4: 'value4.with.dot' }, - ]; - - expect(processLabels(labels)).toEqual({ - keys: ['label1', 'label2', 'label3', 'label4'], - values: { - label1: ['value1'], - label2: ['value2'], - label3: [`value3 with space`], - label4: [`value4.with.dot`], - }, - }); - }); - - it('dont wrap utf8 labels with quotes', () => { - const labels: Array<{ [key: string]: string }> = [ - { 'label1 with space': 'value1' }, - { 'label2.with.dot': 'value2' }, - ]; - - expect(processLabels(labels)).toEqual({ - keys: ['label1 with space', 'label2.with.dot'], - values: { - 'label1 with space': ['value1'], - 'label2.with.dot': ['value2'], - }, - }); - }); -}); - describe('removeQuotesIfExist', () => { it('removes quotes from a string with double quotes', () => { const input = '"hello"'; diff --git a/packages/grafana-prometheus/src/language_utils.ts b/packages/grafana-prometheus/src/language_utils.ts index ee8090c9d44..38b5f364c42 100644 --- a/packages/grafana-prometheus/src/language_utils.ts +++ b/packages/grafana-prometheus/src/language_utils.ts @@ -15,7 +15,7 @@ import { import { addLabelToQuery } from './add_label_to_query'; import { getCacheDurationInMinutes } from './caching'; -import { SUGGESTIONS_LIMIT, PROMETHEUS_QUERY_BUILDER_MAX_RESULTS } from './constants'; +import { PROMETHEUS_QUERY_BUILDER_MAX_RESULTS } from './constants'; import { PrometheusCacheLevel, PromMetricsMetadata, PromMetricsMetadataItem, RecordingRuleIdentifier } from './types'; export const processHistogramMetrics = (metrics: string[]) => { @@ -31,38 +31,6 @@ export const processHistogramMetrics = (metrics: string[]) => { return [...resultSet]; }; -export function processLabels(labels: Array<{ [key: string]: string }>, withName = false) { - // For processing we are going to use sets as they have significantly better performance than arrays - // After we process labels, we will convert sets to arrays and return object with label values in arrays - const valueSet: { [key: string]: Set } = {}; - labels.forEach((label) => { - const { __name__, ...rest } = label; - if (withName) { - valueSet['__name__'] = valueSet['__name__'] || new Set(); - if (!valueSet['__name__'].has(__name__)) { - valueSet['__name__'].add(__name__); - } - } - - Object.keys(rest).forEach((key) => { - if (!valueSet[key]) { - valueSet[key] = new Set(); - } - if (!valueSet[key].has(rest[key])) { - valueSet[key].add(rest[key]); - } - }); - }); - - // valueArray that we are going to return in the object - const valueArray: { [key: string]: string[] } = {}; - limitSuggestions(Object.keys(valueSet)).forEach((key) => { - valueArray[key] = limitSuggestions(Array.from(valueSet[key])); - }); - - return { values: valueArray, keys: Object.keys(valueArray) }; -} - // This will capture 4 groups. Example label filter => {instance="10.4.11.4:9003"} // 1. label: instance // 2. operator: = @@ -276,10 +244,6 @@ function roundSecToNextMin(seconds: number, secondsToRound = 1): number { return Math.ceil(seconds / 60) - (Math.ceil(seconds / 60) % secondsToRound); } -function limitSuggestions(items: string[]) { - return items.slice(0, SUGGESTIONS_LIMIT); -} - const FromPromLikeMap: Record = { '=': AbstractLabelOperator.Equal, '!=': AbstractLabelOperator.NotEqual, diff --git a/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts index 5f604cebfae..aedc041cf71 100644 --- a/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts @@ -15,6 +15,7 @@ export const pluginVersion = "12.4.0-pre"; export interface GaugePanelEffects { barGlow?: boolean; centerGlow?: boolean; + gradient?: boolean; rounded?: boolean; spotlight?: boolean; } @@ -22,6 +23,7 @@ export interface GaugePanelEffects { export const defaultGaugePanelEffects: Partial = { barGlow: false, centerGlow: false, + gradient: true, rounded: false, spotlight: false, }; @@ -29,7 +31,6 @@ export const defaultGaugePanelEffects: Partial = { export interface Options extends common.SingleStatBaseOptions { barWidthFactor: number; effects: GaugePanelEffects; - gradient: ('none' | 'auto'); segmentCount: number; segmentSpacing: number; shape: ('circle' | 'gauge'); @@ -41,7 +42,6 @@ export interface Options extends common.SingleStatBaseOptions { export const defaultOptions: Partial = { barWidthFactor: 0.5, effects: {}, - gradient: 'auto', segmentCount: 1, segmentSpacing: 0.3, shape: 'gauge', diff --git a/packages/grafana-ui/src/components/Layout/Grid/Grid.tsx b/packages/grafana-ui/src/components/Layout/Grid/Grid.tsx index ffba1eb88de..2960de1f502 100644 --- a/packages/grafana-ui/src/components/Layout/Grid/Grid.tsx +++ b/packages/grafana-ui/src/components/Layout/Grid/Grid.tsx @@ -28,7 +28,7 @@ interface PropsWithMinColumnWidth extends GridPropsBase { /** For a responsive layout, fit as many columns while maintaining this minimum column width. * The real width will be calculated based on the theme spacing tokens: `theme.spacing(minColumnWidth)` */ - minColumnWidth?: ResponsiveProp<1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 44 | 55 | 72 | 89 | 144>; + minColumnWidth?: ResponsiveProp<1 | 2 | 3 | 5 | 8 | 13 | 16 | 21 | 34 | 44 | 55 | 72 | 89 | 144>; } /** 'columns' and 'minColumnWidth' are mutually exclusive */ diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx index 96e8b0f50f3..18147e0cac5 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -2,7 +2,13 @@ import { css, cx } from '@emotion/css'; import { isNumber } from 'lodash'; import { useId } from 'react'; -import { DisplayValueAlignmentFactors, FieldDisplay, getDisplayProcessor, GrafanaTheme2 } from '@grafana/data'; +import { + DisplayValueAlignmentFactors, + FieldDisplay, + getDisplayProcessor, + GrafanaTheme2, + TimeRange, +} from '@grafana/data'; import { t } from '@grafana/i18n'; import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; @@ -66,6 +72,7 @@ export interface RadialGaugeProps { showScaleLabels?: boolean; /** For data links */ onClick?: React.MouseEventHandler; + timeRange?: TimeRange; } export type RadialGradientMode = 'none' | 'auto'; diff --git a/packages/grafana-ui/src/components/RadialGauge/effects.tsx b/packages/grafana-ui/src/components/RadialGauge/effects.tsx index 76f15900a14..551d9d91186 100644 --- a/packages/grafana-ui/src/components/RadialGauge/effects.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/effects.tsx @@ -8,7 +8,7 @@ export interface GlowGradientProps { } export function GlowGradient({ id, radius }: GlowGradientProps) { - const glowSize = 0.03 * radius; + const glowSize = 0.02 * radius; return ( diff --git a/packages/grafana-ui/src/components/Sparkline/Sparkline.test.tsx b/packages/grafana-ui/src/components/Sparkline/Sparkline.test.tsx index 9ad678132e9..91f3d9945f3 100644 --- a/packages/grafana-ui/src/components/Sparkline/Sparkline.test.tsx +++ b/packages/grafana-ui/src/components/Sparkline/Sparkline.test.tsx @@ -27,4 +27,48 @@ describe('Sparkline', () => { render() ).not.toThrow(); }); + + it('should not throw an error if there is a single value', () => { + const sparkline: FieldSparkline = { + x: { + name: 'x', + values: [1679839200000], + type: FieldType.time, + config: {}, + }, + y: { + name: 'y', + values: [1], + type: FieldType.number, + config: {}, + state: { + range: { min: 1, max: 1, delta: 0 }, + }, + }, + }; + expect(() => + render() + ).not.toThrow(); + }); + + it('should not throw an error if there are no values', () => { + const sparkline: FieldSparkline = { + x: { + name: 'x', + values: [], + type: FieldType.time, + config: {}, + }, + y: { + name: 'y', + values: [], + type: FieldType.number, + config: {}, + state: {}, + }, + }; + expect(() => + render() + ).not.toThrow(); + }); }); diff --git a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx index 8de8ed1b893..68b1a5a832f 100644 --- a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx +++ b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx @@ -1,32 +1,13 @@ -import { isEqual } from 'lodash'; -import { PureComponent } from 'react'; -import { AlignedData, Range } from 'uplot'; +import React, { memo } from 'react'; -import { - compareDataFrameStructures, - DataFrame, - Field, - FieldConfig, - FieldSparkline, - FieldType, - getFieldColorModeForField, - nullToValue, -} from '@grafana/data'; -import { - AxisPlacement, - GraphDrawStyle, - GraphFieldConfig, - VisibilityMode, - ScaleDirection, - ScaleOrientation, -} from '@grafana/schema'; +import { FieldConfig, FieldSparkline } from '@grafana/data'; +import { GraphFieldConfig } from '@grafana/schema'; import { Themeable2 } from '../../types/theme'; import { UPlotChart } from '../uPlot/Plot'; -import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder'; import { preparePlotData2, getStackingGroups } from '../uPlot/utils'; -import { getYRange, preparePlotFrame } from './utils'; +import { prepareSeries, prepareConfig } from './utils'; export interface SparklineProps extends Themeable2 { width: number; @@ -35,169 +16,18 @@ export interface SparklineProps extends Themeable2 { sparkline: FieldSparkline; } -interface State { - data: AlignedData; - alignedDataFrame: DataFrame; - configBuilder: UPlotConfigBuilder; -} +export const Sparkline: React.FC = memo((props) => { + const { sparkline, config: fieldConfig, theme, width, height } = props; -const defaultConfig: GraphFieldConfig = { - drawStyle: GraphDrawStyle.Line, - showPoints: VisibilityMode.Auto, - axisPlacement: AxisPlacement.Hidden, - pointSize: 2, -}; - -/** @internal */ -export class Sparkline extends PureComponent { - constructor(props: SparklineProps) { - super(props); - - const alignedDataFrame = preparePlotFrame(props.sparkline, props.config); - - this.state = { - data: preparePlotData2(alignedDataFrame, getStackingGroups(alignedDataFrame)), - alignedDataFrame, - configBuilder: this.prepareConfig(alignedDataFrame), - }; + const { frame: alignedDataFrame, warning } = prepareSeries(sparkline, fieldConfig); + if (warning) { + return null; } - static getDerivedStateFromProps(props: SparklineProps, state: State) { - const _frame = preparePlotFrame(props.sparkline, props.config); - const frame = nullToValue(_frame); - if (!frame) { - return { ...state }; - } + const data = preparePlotData2(alignedDataFrame, getStackingGroups(alignedDataFrame)); + const configBuilder = prepareConfig(sparkline, alignedDataFrame, theme); - return { - ...state, - data: preparePlotData2(frame, getStackingGroups(frame)), - alignedDataFrame: frame, - }; - } + return ; +}); - componentDidUpdate(prevProps: SparklineProps, prevState: State) { - const { alignedDataFrame } = this.state; - - if (!alignedDataFrame) { - return; - } - - let rebuildConfig = false; - - if (prevProps.sparkline !== this.props.sparkline) { - const isStructureChanged = !compareDataFrameStructures(this.state.alignedDataFrame, prevState.alignedDataFrame); - const isRangeChanged = !isEqual( - alignedDataFrame.fields[1].state?.range, - prevState.alignedDataFrame.fields[1].state?.range - ); - rebuildConfig = isStructureChanged || isRangeChanged; - } else { - rebuildConfig = !isEqual(prevProps.config, this.props.config); - } - - if (rebuildConfig) { - this.setState({ configBuilder: this.prepareConfig(alignedDataFrame) }); - } - } - - getYRange(field: Field): Range.MinMax { - return getYRange(field, this.state.alignedDataFrame); - } - - prepareConfig(data: DataFrame) { - const { theme } = this.props; - const builder = new UPlotConfigBuilder(); - - builder.setCursor({ - show: false, - x: false, // no crosshairs - y: false, - }); - - // X is the first field in the alligned frame - const xField = data.fields[0]; - builder.addScale({ - scaleKey: 'x', - orientation: ScaleOrientation.Horizontal, - direction: ScaleDirection.Right, - isTime: false, //xField.type === FieldType.time, - range: () => { - const { sparkline } = this.props; - if (sparkline.x) { - if (sparkline.timeRange && sparkline.x.type === FieldType.time) { - return [sparkline.timeRange.from.valueOf(), sparkline.timeRange.to.valueOf()]; - } - const vals = sparkline.x.values; - return [vals[0], vals[vals.length - 1]]; - } - return [0, sparkline.y.values.length - 1]; - }, - }); - - builder.addAxis({ - scaleKey: 'x', - theme, - placement: AxisPlacement.Hidden, - }); - - for (let i = 0; i < data.fields.length; i++) { - const field = data.fields[i]; - const config: FieldConfig = field.config; - const customConfig: GraphFieldConfig = { - ...defaultConfig, - ...config.custom, - }; - - if (field === xField || field.type !== FieldType.number) { - continue; - } - - const scaleKey = config.unit || '__fixed'; - builder.addScale({ - scaleKey, - orientation: ScaleOrientation.Vertical, - direction: ScaleDirection.Up, - range: () => this.getYRange(field), - }); - - builder.addAxis({ - scaleKey, - theme, - placement: AxisPlacement.Hidden, - }); - - const colorMode = getFieldColorModeForField(field); - const seriesColor = colorMode.getCalculator(field, theme)(0, 0); - const pointsMode = - customConfig.drawStyle === GraphDrawStyle.Points ? VisibilityMode.Always : customConfig.showPoints; - - builder.addSeries({ - pxAlign: false, - scaleKey, - theme, - colorMode, - thresholds: config.thresholds, - drawStyle: customConfig.drawStyle!, - lineColor: customConfig.lineColor ?? seriesColor, - lineWidth: customConfig.lineWidth, - lineInterpolation: customConfig.lineInterpolation, - showPoints: pointsMode, - pointSize: customConfig.pointSize, - fillOpacity: customConfig.fillOpacity, - fillColor: customConfig.fillColor, - lineStyle: customConfig.lineStyle, - gradientMode: customConfig.gradientMode, - spanNulls: customConfig.spanNulls, - }); - } - - return builder; - } - - render() { - const { data, configBuilder } = this.state; - const { width, height } = this.props; - return ; - } -} +Sparkline.displayName = 'Sparkline'; diff --git a/packages/grafana-ui/src/components/Sparkline/utils.test.ts b/packages/grafana-ui/src/components/Sparkline/utils.test.ts index 9eb914af438..2a77677d998 100644 --- a/packages/grafana-ui/src/components/Sparkline/utils.test.ts +++ b/packages/grafana-ui/src/components/Sparkline/utils.test.ts @@ -114,6 +114,13 @@ describe('Get y range', () => { config: {}, state: { range: { min: -2, max: -2, delta: 0 } }, }; + const decimalsCloseYField: Field = { + name: 'y', + values: [2, 1.999999999999999, 2.000000000000001, 2, 2], + type: FieldType.number, + config: {}, + state: { range: { min: 1.999999999999999, max: 2.000000000000001, delta: 0 } }, + }; const xField: Field = { name: 'x', values: [1000, 2000, 3000, 4000, 5000], @@ -154,12 +161,12 @@ describe('Get y range', () => { { description: 'straight line', field: straightLineYField, - expected: [0, 4], + expected: [2, 4], }, { description: 'straight line, negative values', field: straightLineNegYField, - expected: [-4, 0], + expected: [-4, -2], }, { description: 'straight line with config min and max', @@ -171,8 +178,13 @@ describe('Get y range', () => { field: { ...straightLineYField, config: { noValue: '0' } }, expected: [0, 2], }, + { + description: 'long decimals which are nearly equal and result in a functional delta of 0', + field: decimalsCloseYField, + expected: [2, 4], + }, ])(`should return correct range for $description`, ({ field, expected }) => { - const actual = getYRange(field, getAlignedFrame(field)); + const actual = getYRange(getAlignedFrame(field)); expect(actual).toEqual(expected); expect(actual[0]).toBeLessThan(actual[1]!); }); diff --git a/packages/grafana-ui/src/components/Sparkline/utils.ts b/packages/grafana-ui/src/components/Sparkline/utils.ts index 5c2afd8a1cb..e4d17a85c15 100644 --- a/packages/grafana-ui/src/components/Sparkline/utils.ts +++ b/packages/grafana-ui/src/components/Sparkline/utils.ts @@ -1,16 +1,29 @@ import { Range } from 'uplot'; import { + applyNullInsertThreshold, DataFrame, FieldConfig, FieldSparkline, FieldType, + getFieldColorModeForField, + GrafanaTheme2, isLikelyAscendingVector, + nullToValue, + roundDecimals, sortDataFrame, - applyNullInsertThreshold, - Field, } from '@grafana/data'; -import { GraphFieldConfig } from '@grafana/schema'; +import { t } from '@grafana/i18n'; +import { + AxisPlacement, + GraphDrawStyle, + GraphFieldConfig, + VisibilityMode, + ScaleDirection, + ScaleOrientation, +} from '@grafana/schema'; + +import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder'; /** @internal * Given a sparkline config returns a DataFrame ready to be turned into Plot data set @@ -55,33 +68,176 @@ export function preparePlotFrame(sparkline: FieldSparkline, config?: FieldConfig /** * apply configuration defaults and ensure that the range is never two equal values. */ -export function getYRange(field: Field, alignedFrame: DataFrame): Range.MinMax { - let { min, max } = alignedFrame.fields[1].state?.range!; +export function getYRange(alignedFrame: DataFrame): Range.MinMax { + const field = alignedFrame.fields[1]; + let { min, max } = field.state?.range!; - // enure that the min/max from the field config are respected - min = Math.max(min!, field.config.min ?? -Infinity); - max = Math.min(max!, field.config.max ?? Infinity); + // enure that the min/max from the field config are respected. + min = Math.min(min!, field.config.min ?? Infinity); + max = Math.max(max!, field.config.max ?? -Infinity); + + // console.log({ min, max }); // if noValue is set, ensure that it is included in the range as well - const noValue = +alignedFrame.fields[1].config?.noValue!; + const noValue = +field.config?.noValue!; if (!Number.isNaN(noValue)) { min = Math.min(min, noValue); max = Math.max(max, noValue); } - // if min and max are equal after all of that, create a range - // that allows the sparkline to be visible in the center of the viz - if (min === max) { - if (min === 0) { - max = 100; - } else if (min < 0) { - max = 0; - min *= 2; - } else { - min = 0; - max *= 2; - } + // call roundDecimals to mirror what is going to eventually happen in uplot + let roundedMin = roundDecimals(min, field.config.decimals ?? 0); + let roundedMax = roundDecimals(max, field.config.decimals ?? 0); + + // if the rounded min and max are different, + // we can return the real min and max. + if (roundedMin !== roundedMax) { + return [min, max]; } - return [min, max]; + // we are forced to tweak the min and max since they + // will be treated as equal after rounding by uPlot. + if (roundedMin === 0) { + // both are zero + roundedMax = 1; + } else if (roundedMin < 0) { + // both are negative + // max = 0; + roundedMin *= 2; + } else { + // both are positive + // min = 0; + roundedMax *= 2; + } + + return [roundedMin, roundedMax]; } + +// TODO: #112977 enable highlight index +// const HIGHLIGHT_IDX_POINT_SIZE = 6; + +const defaultConfig: GraphFieldConfig = { + drawStyle: GraphDrawStyle.Line, + showPoints: VisibilityMode.Auto, + axisPlacement: AxisPlacement.Hidden, + pointSize: 2, +}; + +export const prepareSeries = ( + sparkline: FieldSparkline, + fieldConfig?: FieldConfig +): { frame: DataFrame; warning?: string } => { + const frame = nullToValue(preparePlotFrame(sparkline, fieldConfig)); + if (frame.fields.some((f) => f.values.length <= 1)) { + return { + warning: t( + 'grafana-ui.components.sparkline.warning.too-few-values', + 'Sparkline requires at least two values to render.' + ), + frame, + }; + } + return { frame }; +}; + +export const prepareConfig = ( + sparkline: FieldSparkline, + dataFrame: DataFrame, + theme: GrafanaTheme2 +): UPlotConfigBuilder => { + const builder = new UPlotConfigBuilder(); + // const rangePad = HIGHLIGHT_IDX_POINT_SIZE / 2; + + builder.setCursor({ + show: false, + x: false, // no crosshairs + y: false, + }); + + // X is the first field in the aligned frame + const xField = dataFrame.fields[0]; + builder.addScale({ + scaleKey: 'x', + orientation: ScaleOrientation.Horizontal, + direction: ScaleDirection.Right, + isTime: false, // xField.type === FieldType.time, + range: () => { + if (sparkline.x) { + if (sparkline.timeRange && sparkline.x.type === FieldType.time) { + return [sparkline.timeRange.from.valueOf(), sparkline.timeRange.to.valueOf()]; + } + const vals = sparkline.x.values; + return [vals[0], vals[vals.length - 1]]; + } + return [0, sparkline.y.values.length - 1]; + }, + }); + + builder.addAxis({ + scaleKey: 'x', + theme, + placement: AxisPlacement.Hidden, + }); + + for (let i = 0; i < dataFrame.fields.length; i++) { + const field = dataFrame.fields[i]; + const config: FieldConfig = field.config; + const customConfig: GraphFieldConfig = { + ...defaultConfig, + ...config.custom, + }; + + if (field === xField || field.type !== FieldType.number) { + continue; + } + + const scaleKey = config.unit || '__fixed'; + builder.addScale({ + scaleKey, + orientation: ScaleOrientation.Vertical, + direction: ScaleDirection.Up, + range: () => getYRange(dataFrame), + }); + + builder.addAxis({ + scaleKey, + theme, + placement: AxisPlacement.Hidden, + }); + + const colorMode = getFieldColorModeForField(field); + const seriesColor = colorMode.getCalculator(field, theme)(0, 0); + // TODO: #112977 enable highlight index and adjust padding accordingly + // const hasHighlightIndex = typeof sparkline.highlightIndex === 'number'; + // if (hasHighlightIndex) { + // builder.setPadding([rangePad, rangePad, rangePad, rangePad]); + // } + const pointsMode = + customConfig.drawStyle === GraphDrawStyle.Points // || hasHighlightIndex + ? VisibilityMode.Always + : customConfig.showPoints; + + builder.addSeries({ + pxAlign: false, + scaleKey, + theme, + colorMode, + thresholds: config.thresholds, + drawStyle: customConfig.drawStyle!, + lineColor: customConfig.lineColor ?? seriesColor, + lineWidth: customConfig.lineWidth, + lineInterpolation: customConfig.lineInterpolation, + showPoints: pointsMode, + // TODO: #112977 enable highlight index + pointSize: /* hasHighlightIndex ? HIGHLIGHT_IDX_POINT_SIZE : */ customConfig.pointSize, + // pointsFilter: hasHighlightIndex ? [sparkline.highlightIndex!] : undefined, + fillOpacity: customConfig.fillOpacity, + fillColor: customConfig.fillColor, + lineStyle: customConfig.lineStyle, + gradientMode: customConfig.gradientMode, + spanNulls: customConfig.spanNulls, + }); + } + + return builder; +}; diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 5ebbca411eb..3b764568e75 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -380,13 +380,6 @@ func (hs *HTTPServer) deleteDashboard(c *contextmodel.ReqContext) response.Respo return dashboardErrResponse(err, "Failed to delete dashboard") } - if hs.Live != nil { - err := hs.Live.GrafanaScope.Dashboards.DashboardDeleted(c.GetOrgID(), c.SignedInUser, dash.UID) - if err != nil { - hs.log.Error("Failed to broadcast delete info", "dashboard", dash.UID, "error", err) - } - } - return response.JSON(http.StatusOK, util.DynMap{ "title": dash.Title, "message": fmt.Sprintf("Dashboard %s deleted", dash.Title), @@ -482,31 +475,6 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S } dashboard, saveErr := hs.DashboardService.SaveDashboard(ctx, dashItem, allowUiUpdate) - - if hs.Live != nil { - // Tell everyone listening that the dashboard changed - if dashboard == nil { - dashboard = dash // the original request - } - - // This will broadcast all save requests only if a `gitops` observer exists. - // gitops is useful when trying to save dashboards in an environment where the user can not save - channel := hs.Live.GrafanaScope.Dashboards - liveerr := channel.DashboardSaved(c.GetOrgID(), c.SignedInUser, cmd.Message, dashboard, saveErr) - - // When an error exists, but the value broadcast to a gitops listener return 202 - if liveerr == nil && saveErr != nil && channel.HasGitOpsObserver(c.GetOrgID()) { - return response.JSON(http.StatusAccepted, util.DynMap{ - "status": "pending", - "message": "changes were broadcast to the gitops listener", - }) - } - - if liveerr != nil { - hs.log.Warn("Unable to broadcast save event", "uid", dashboard.UID, "error", liveerr) - } - } - if saveErr != nil { return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, saveErr) } diff --git a/pkg/operators/iam/zanzana_folder_reconciler.go b/pkg/operators/iam/zanzana_folder_reconciler.go index 0f2fb287bb4..ee2233d7877 100644 --- a/pkg/operators/iam/zanzana_folder_reconciler.go +++ b/pkg/operators/iam/zanzana_folder_reconciler.go @@ -11,15 +11,16 @@ import ( "os/signal" "syscall" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/client-go/rest" + "k8s.io/client-go/transport" + "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/operator" folder "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" "github.com/grafana/grafana/apps/iam/pkg/app" "github.com/grafana/grafana/pkg/server" "github.com/grafana/grafana/pkg/setting" - "github.com/prometheus/client_golang/prometheus" - "k8s.io/client-go/rest" - "k8s.io/client-go/transport" "github.com/grafana/authlib/authn" utilnet "k8s.io/apimachinery/pkg/util/net" @@ -95,7 +96,7 @@ func buildIAMConfigFromSettings(cfg *setting.Cfg, registerer prometheus.Register if zanzanaURL == "" { return nil, fmt.Errorf("zanzana_url is required in [operator] section") } - iamCfg.AppConfig.ZanzanaClientCfg.URL = zanzanaURL + iamCfg.AppConfig.ZanzanaClientCfg.Addr = zanzanaURL iamCfg.AppConfig.InformerConfig.MaxConcurrentWorkers = operatorSec.Key("max_concurrent_workers").MustUint64(20) diff --git a/pkg/registry/apis/collections/legacy/stars.go b/pkg/registry/apis/collections/legacy/stars.go index 8c7d9e711ea..39f7a9c088f 100644 --- a/pkg/registry/apis/collections/legacy/stars.go +++ b/pkg/registry/apis/collections/legacy/stars.go @@ -204,11 +204,14 @@ func (s *DashboardStarsStorage) write(ctx context.Context, obj *collections.Star previous[v] = true } } - for _, dashboard := range stars { + for idx, dashboard := range stars { if previous[dashboard] { delete(previous, dashboard) continue // nothing needed } + if idx > 0 { + time.Sleep(75 * time.Millisecond) // values are ordered by update time; this keeps the order predictable + } err = s.stars.Add(ctx, &star.StarDashboardCommand{ UserID: user.ID, OrgID: user.OrgID, diff --git a/pkg/registry/apis/collections/register.go b/pkg/registry/apis/collections/register.go index 9be4412c377..699d3bbe672 100644 --- a/pkg/registry/apis/collections/register.go +++ b/pkg/registry/apis/collections/register.go @@ -46,12 +46,6 @@ func RegisterAPIService( users user.Service, apiregistration builder.APIRegistrar, ) *APIBuilder { - // Requires development settings and clearly experimental - //nolint:staticcheck // not yet migrated to OpenFeature - if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { - return nil - } - sql := legacy.NewLegacySQL(legacysql.NewDatabaseProvider(db)) builder := &APIBuilder{ authorizer: &utils.AuthorizeFromName{ diff --git a/pkg/registry/apis/dashboard/dashboard_storage.go b/pkg/registry/apis/dashboard/dashboard_storage.go index d2d54c8d815..7ab91f8ec66 100644 --- a/pkg/registry/apis/dashboard/dashboard_storage.go +++ b/pkg/registry/apis/dashboard/dashboard_storage.go @@ -7,20 +7,41 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" + "github.com/grafana/grafana-app-sdk/logging" grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/live" ) -// dashboardStoragePermissionWrapper is a wrapper around the grafanarest.Storage that adds dashboard permissions handling -// when dual writing is enabled. -type dashboardStoragePermissionWrapper struct { - dashboardPermissionsSvc accesscontrol.DashboardPermissionsService +// dashboardStorageWrapper is a wrapper around the grafanarest.Storage so it will: +// 1. support adds dashboard permissions handling +// 2. broadcast changes to grafana live +// when running in single tenant mode +type dashboardStorageWrapper struct { grafanarest.Storage + + dashboardPermissionsSvc accesscontrol.DashboardPermissionsService + live live.DashboardActivityChannel } -func (d dashboardStoragePermissionWrapper) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { - info, err := request.NamespaceInfoFrom(ctx, true) +func (d dashboardStorageWrapper) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) { + ns, err := request.NamespaceInfoFrom(ctx, true) + if err != nil { + return nil, false, err + } + + obj, created, err := d.Storage.Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options) + if err == nil && ns.OrgID > 0 && d.live != nil { + if err := d.live.DashboardSaved(ns.OrgID, name); err != nil { + logging.FromContext(ctx).Info("live dashboard update failed", "err", err) + } + } + return obj, created, err +} + +func (d dashboardStorageWrapper) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) { + ns, err := request.NamespaceInfoFrom(ctx, true) if err != nil { return nil, false, err } @@ -28,7 +49,12 @@ func (d dashboardStoragePermissionWrapper) Delete(ctx context.Context, name stri if err != nil { return obj, async, err } - if accessErr := d.dashboardPermissionsSvc.DeleteResourcePermissions(ctx, info.OrgID, name); accessErr != nil { + if ns.OrgID > 0 && d.live != nil { + if err := d.live.DashboardDeleted(ns.OrgID, name); err != nil { + logging.FromContext(ctx).Info("live dashboard update failed", "err", err) + } + } + if accessErr := d.dashboardPermissionsSvc.DeleteResourcePermissions(ctx, ns.OrgID, name); accessErr != nil { return obj, async, accessErr } return obj, async, nil diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index 8fbad6042ad..bd732a70b08 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -56,6 +56,7 @@ import ( "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/libraryelements" "github.com/grafana/grafana/pkg/services/librarypanels" + "github.com/grafana/grafana/pkg/services/live" "github.com/grafana/grafana/pkg/services/provisioning" "github.com/grafana/grafana/pkg/services/publicdashboards" "github.com/grafana/grafana/pkg/services/quota" @@ -122,6 +123,7 @@ type DashboardsAPIBuilder struct { snapshotService dashboardsnapshots.Service snapshotOptions dashv0.SnapshotSharingOptions namespacer request.NamespaceMapper + dashboardActivityChannel live.DashboardActivityChannel isStandalone bool // skips any handling including anything to do with legacy storage } @@ -151,6 +153,7 @@ func RegisterAPIService( libraryPanels libraryelements.Service, publicDashboardService publicdashboards.Service, snapshotService dashboardsnapshots.Service, + dashboardActivityChannel live.DashboardActivityChannel, ) *DashboardsAPIBuilder { dbp := legacysql.NewDatabaseProvider(sql) namespacer := request.GetNamespaceMapper(cfg) @@ -185,6 +188,7 @@ func RegisterAPIService( snapshotService: snapshotService, snapshotOptions: snapshotOptions, namespacer: namespacer, + dashboardActivityChannel: dashboardActivityChannel, legacy: &DashboardStorage{ Access: legacy.NewDashboardSQLAccess(dbp, namespacer, dashStore, provisioning, libraryPanelSvc, sorter, dashboardPermissionsSvc, accessControl, features), DashboardService: dashboardService, @@ -696,9 +700,10 @@ func (b *DashboardsAPIBuilder) storageForVersion( if err != nil { return err } - storage[dashboards.StoragePath()] = dashboardStoragePermissionWrapper{ - dashboardPermissionsSvc: b.dashboardPermissionsSvc, + storage[dashboards.StoragePath()] = dashboardStorageWrapper{ Storage: dw, + dashboardPermissionsSvc: b.dashboardPermissionsSvc, + live: b.dashboardActivityChannel, } // Register the DTO endpoint that will consolidate all dashboard bits @@ -775,11 +780,6 @@ func (b *DashboardsAPIBuilder) afterDelete(obj runtime.Object, _ *metav1.DeleteO } var defaultDashboardPermissions = []map[string]any{ - { - "kind": "BasicRole", - "name": "Admin", - "verb": "admin", - }, { "kind": "BasicRole", "name": "Editor", diff --git a/pkg/registry/apis/folders/register.go b/pkg/registry/apis/folders/register.go index 72a4abf8e0a..64e51c06312 100644 --- a/pkg/registry/apis/folders/register.go +++ b/pkg/registry/apis/folders/register.go @@ -203,11 +203,6 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API } var defaultPermissions = []map[string]any{ - { - "kind": "BasicRole", - "name": "Admin", - "verb": "admin", - }, { "kind": "BasicRole", "name": "Editor", diff --git a/pkg/registry/apis/preferences/register.go b/pkg/registry/apis/preferences/register.go index 8964b0e0cd7..e0e6ff947fc 100644 --- a/pkg/registry/apis/preferences/register.go +++ b/pkg/registry/apis/preferences/register.go @@ -42,12 +42,6 @@ func RegisterAPIService( users user.Service, apiregistration builder.APIRegistrar, ) *APIBuilder { - // Requires development settings and clearly experimental - //nolint:staticcheck // not yet migrated to OpenFeature - if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { - return nil - } - sql := legacy.NewLegacySQL(legacysql.NewDatabaseProvider(db)) builder := &APIBuilder{ merger: newMerger(cfg, sql), diff --git a/pkg/registry/apis/query/query.go b/pkg/registry/apis/query/query.go index 99323eb2721..ad9c9ac9888 100644 --- a/pkg/registry/apis/query/query.go +++ b/pkg/registry/apis/query/query.go @@ -10,6 +10,8 @@ import ( "strconv" "strings" + "github.com/grafana/authlib/authn" + claims "github.com/grafana/authlib/types" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" "github.com/grafana/grafana/pkg/api/dtos" @@ -154,11 +156,11 @@ func (r *queryREST) Connect(connectCtx context.Context, name string, _ runtime.O } } } - connectLogger.Debug("responder sending status code", "statusCode", statusCode) + connectLogger.Debug("responder sending status code", "statusCode", statusCode, "caller", getCaller(ctx)) }, func(err error) { - connectLogger.Error("error caught in handler", "err", err) + connectLogger.Error("error caught in handler", "err", err, "caller", getCaller(ctx)) span.SetStatus(codes.Error, "query error") if err == nil { @@ -480,3 +482,12 @@ func getValidDataSourceRef(ctx context.Context, ds *v0alpha1.DataSourceRef, id i return ds, nil } + +func getCaller(ctx context.Context) string { + authInfo, ok := claims.AuthInfoFrom(ctx) + if !ok { + return "" + } else { + return strings.Join(authInfo.GetExtra()[authn.ServiceIdentityKey], ",") + } +} diff --git a/pkg/registry/apis/query/query_test.go b/pkg/registry/apis/query/query_test.go index b23c78d55bc..56025d3893b 100644 --- a/pkg/registry/apis/query/query_test.go +++ b/pkg/registry/apis/query/query_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + claims "github.com/grafana/authlib/types" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" dataapi "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" @@ -180,7 +181,7 @@ func TestQueryAPI(t *testing.T) { legacyDatasourceLookup: &mockLegacyDataSourceLookup{}, } - reqCtx := identity.WithRequester(context.Background(), mockUser{}) + reqCtx := claims.WithAuthInfo(identity.WithRequester(context.Background(), mockUser{}), &mockAuthInfo{}) req := httptest.NewRequestWithContext(reqCtx, http.MethodPost, "/some-path", bytes.NewReader([]byte(tc.queryJSON))) req.Header.Set("Content-Type", "application/json") @@ -439,3 +440,11 @@ func TestMergeHeaders(t *testing.T) { }) } } + +type mockAuthInfo struct { + claims.AuthInfo +} + +func (main mockAuthInfo) GetExtra() map[string][]string { + return nil +} diff --git a/pkg/registry/apis/service/register.go b/pkg/registry/apis/service/register.go index 7f51ad844e0..1002bff5bf6 100644 --- a/pkg/registry/apis/service/register.go +++ b/pkg/registry/apis/service/register.go @@ -12,6 +12,7 @@ import ( service "github.com/grafana/grafana/pkg/apis/service/v0alpha1" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" + roleauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/featuremgmt" ) @@ -37,7 +38,7 @@ func RegisterAPIService(features featuremgmt.FeatureToggles, apiregistration bui } func (b *ServiceAPIBuilder) GetAuthorizer() authorizer.Authorizer { - return nil // default authorizer is fine + return roleauthorizer.NewRoleAuthorizer() } func (b *ServiceAPIBuilder) GetGroupVersion() schema.GroupVersion { diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 0d4bb10c0b5..970d1c003c9 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -280,6 +280,7 @@ var wireBasicSet = wire.NewSet( store.ProvideService, store.ProvideSystemUsersService, live.ProvideService, + live.ProvideDashboardActivityChannel, pushhttp.ProvideService, contexthandler.ProvideService, ldapservice.ProvideService, diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 8500130aeea..1406e6fdbf8 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -870,7 +870,8 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService) ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService) apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) - dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl) + dashboardActivityChannel := live.ProvideDashboardActivityChannel(grafanaLive) + dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl, dashboardActivityChannel) dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService) if err != nil { return nil, err @@ -1527,7 +1528,8 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService) ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService) apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService) - dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl) + dashboardActivityChannel := live.ProvideDashboardActivityChannel(grafanaLive) + dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl, dashboardActivityChannel) dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService) if err != nil { return nil, err @@ -1791,7 +1793,7 @@ var withOTelSet = wire.NewSet( otelTracer, grpcserver.ProvideService, interceptors.ProvideAuthenticator, ) -var wireBasicSet = wire.NewSet(annotationsimpl.ProvideService, wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)), New, api.ProvideHTTPServer, query.ProvideService, wire.Bind(new(query.Service), new(*query.ServiceImpl)), bus.ProvideBus, wire.Bind(new(bus.Bus), new(*bus.InProcBus)), rendering.ProvideService, wire.Bind(new(rendering.Service), new(*rendering.RenderingService)), routing.ProvideRegister, wire.Bind(new(routing.RouteRegister), new(*routing.RouteRegisterImpl)), hooks.ProvideService, kvstore.ProvideService, localcache.ProvideService, bundleregistry.ProvideService, wire.Bind(new(supportbundles.Service), new(*bundleregistry.Service)), updatemanager.ProvideGrafanaService, updatemanager.ProvidePluginsService, service.ProvideService, wire.Bind(new(usagestats.Service), new(*service.UsageStats)), validator3.ProvideService, provisioning.ProvideStubProvisioningService, legacy.ProvideMigratorDashboardAccessor, migrations2.ProvideUnifiedMigrator, pluginsintegration.WireSet, dashboards.ProvideFileStoreManager, wire.Bind(new(dashboards.FileStore), new(*dashboards.FileStoreManager)), cloudwatch.ProvideService, cloudmonitoring.ProvideService, azuremonitor.ProvideService, postgres.ProvideService, mysql.ProvideService, mssql.ProvideService, store.ProvideEntityEventsService, dualwrite.ProvideService, httpclientprovider.New, wire.Bind(new(httpclient.Provider), new(*httpclient2.Provider)), serverlock.ProvideService, wire.Bind(new(installsync.ServerLock), new(*serverlock.ServerLockService)), annotationsimpl.ProvideCleanupService, wire.Bind(new(annotations.Cleaner), new(*annotationsimpl.CleanupServiceImpl)), cleanup.ProvideService, shorturlimpl.ProvideService, wire.Bind(new(shorturls.Service), new(*shorturlimpl.ShortURLService)), queryhistory.ProvideService, wire.Bind(new(queryhistory.Service), new(*queryhistory.QueryHistoryService)), correlations.ProvideService, wire.Bind(new(correlations.Service), new(*correlations.CorrelationsService)), quotaimpl.ProvideService, remotecache.ProvideService, wire.Bind(new(remotecache.CacheStorage), new(*remotecache.RemoteCache)), authinfoimpl.ProvideService, wire.Bind(new(login.AuthInfoService), new(*authinfoimpl.Service)), authinfoimpl.ProvideStore, datasourceproxy.ProvideService, sort.ProvideService, search2.ProvideService, searchV2.ProvideService, searchV2.ProvideSearchHTTPService, store.ProvideService, store.ProvideSystemUsersService, live.ProvideService, pushhttp.ProvideService, contexthandler.ProvideService, service12.ProvideService, wire.Bind(new(service12.LDAP), new(*service12.LDAPImpl)), jwt.ProvideService, wire.Bind(new(jwt.JWTService), new(*jwt.AuthService)), store2.ProvideDBStore, image.ProvideDeleteExpiredService, ngalert.ProvideService, librarypanels.ProvideService, wire.Bind(new(librarypanels.Service), new(*librarypanels.LibraryPanelService)), libraryelements.ProvideService, wire.Bind(new(libraryelements.Service), new(*libraryelements.LibraryElementService)), notifications.ProvideService, notifications.ProvideSmtpService, github.ProvideFactory, tracing.ProvideService, tracing.ProvideTracingConfig, wire.Bind(new(tracing.Tracer), new(*tracing.TracingService)), withOTelSet, testdatasource.ProvideService, api4.ProvideService, opentsdb.ProvideService, socialimpl.ProvideService, influxdb.ProvideService, wire.Bind(new(social.Service), new(*socialimpl.SocialService)), tempo.ProvideService, loki.ProvideService, graphite.ProvideService, prometheus.ProvideService, elasticsearch.ProvideService, pyroscope.ProvideService, parca.ProvideService, zipkin.ProvideService, jaeger.ProvideService, service9.ProvideCacheService, wire.Bind(new(datasources.CacheService), new(*service9.CacheServiceImpl)), service2.ProvideEncryptionService, wire.Bind(new(encryption2.Internal), new(*service2.Service)), manager.ProvideSecretsService, wire.Bind(new(secrets.Service), new(*manager.SecretsService)), database.ProvideSecretsStore, wire.Bind(new(secrets.Store), new(*database.SecretsStoreImpl)), garbagecollectionworker.ProvideWorker, grafanads.ProvideService, wire.Bind(new(dashboardsnapshots.Store), new(*database5.DashboardSnapshotStore)), database5.ProvideStore, wire.Bind(new(dashboardsnapshots.Service), new(*service10.ServiceImpl)), service10.ProvideService, service9.ProvideService, wire.Bind(new(datasources.DataSourceService), new(*service9.Service)), service9.ProvideLegacyDataSourceLookup, retriever.ProvideService, wire.Bind(new(serviceaccounts.ServiceAccountRetriever), new(*retriever.Service)), ossaccesscontrol.ProvideServiceAccountPermissions, wire.Bind(new(accesscontrol.ServiceAccountPermissionsService), new(*ossaccesscontrol.ServiceAccountPermissionsService)), manager3.ProvideServiceAccountsService, proxy.ProvideServiceAccountsProxy, wire.Bind(new(serviceaccounts.Service), new(*proxy.ServiceAccountsProxy)), dsquerierclient.NewNullQSDatasourceClientBuilder, expr.ProvideService, featuremgmt.ProvideManagerService, featuremgmt.ProvideToggles, service7.ProvideDashboardServiceImpl, wire.Bind(new(dashboards2.PermissionsRegistrationService), new(*service7.DashboardServiceImpl)), service7.ProvideDashboardService, service7.ProvideDashboardProvisioningService, service7.ProvideDashboardPluginService, database2.ProvideDashboardStore, folderimpl.ProvideService, wire.Bind(new(folder.Service), new(*folderimpl.Service)), wire.Bind(new(folder.LegacyService), new(*folderimpl.Service)), folderimpl.ProvideStore, wire.Bind(new(folder.Store), new(*folderimpl.FolderStoreImpl)), service11.ProvideService, wire.Bind(new(dashboardimport.Service), new(*service11.ImportDashboardService)), service8.ProvideService, wire.Bind(new(plugindashboards.Service), new(*service8.Service)), service8.ProvideDashboardUpdater, kvstore2.ProvideService, avatar.ProvideAvatarCacheServer, statscollector.ProvideService, csrf.ProvideCSRFFilter, wire.Bind(new(csrf.Service), new(*csrf.CSRF)), ossaccesscontrol.ProvideTeamPermissions, wire.Bind(new(accesscontrol.TeamPermissionsService), new(*ossaccesscontrol.TeamPermissionsService)), ossaccesscontrol.ProvideFolderPermissions, wire.Bind(new(accesscontrol.FolderPermissionsService), new(*ossaccesscontrol.FolderPermissionsService)), ossaccesscontrol.ProvideDashboardPermissions, wire.Bind(new(accesscontrol.DashboardPermissionsService), new(*ossaccesscontrol.DashboardPermissionsService)), ossaccesscontrol.ProvideReceiverPermissionsService, wire.Bind(new(accesscontrol.ReceiverPermissionsService), new(*ossaccesscontrol.ReceiverPermissionsService)), starimpl.ProvideService, playlistimpl.ProvideService, apikeyimpl.ProvideService, dashverimpl.ProvideService, service3.ProvideService, wire.Bind(new(publicdashboards.Service), new(*service3.PublicDashboardServiceImpl)), database3.ProvideStore, wire.Bind(new(publicdashboards.Store), new(*database3.PublicDashboardStoreImpl)), metric.ProvideService, api2.ProvideApi, api3.ProvideApi, userimpl.ProvideService, orgimpl.ProvideService, orgimpl.ProvideDeletionService, statsimpl.ProvideService, grpccontext.ProvideContextHandler, grpcserver.ProvideHealthService, grpcserver.ProvideReflectionService, resolver.ProvideEntityReferenceResolver, teamimpl.ProvideService, teamapi.ProvideTeamAPI, tempuserimpl.ProvideService, loginattemptimpl.ProvideService, wire.Bind(new(loginattempt.Service), new(*loginattemptimpl.Service)), migrations3.ProvideDataSourceMigrationService, migrations3.ProvideSecretMigrationProvider, wire.Bind(new(migrations3.SecretMigrationProvider), new(*migrations3.SecretMigrationProviderImpl)), promtypemigration.ProvideAzurePromMigrationService, promtypemigration.ProvideAmazonPromMigrationService, promtypemigration.ProvidePromTypeMigrationProvider, wire.Bind(new(promtypemigration.PromTypeMigrationProvider), new(*promtypemigration.PromTypeMigrationProviderImpl)), resourcepermissions.NewActionSetService, wire.Bind(new(accesscontrol.ActionResolver), new(resourcepermissions.ActionSetService)), wire.Bind(new(pluginaccesscontrol.ActionSetRegistry), new(resourcepermissions.ActionSetService)), permreg.ProvidePermissionRegistry, acimpl.ProvideAccessControl, accesscontrol.ProvideFixedRolesLoader, dualwrite2.ProvideZanzanaReconciler, navtreeimpl.ProvideService, wire.Bind(new(accesscontrol.AccessControl), new(*acimpl.AccessControl)), wire.Bind(new(notifications.TempUserStore), new(tempuser.Service)), tagimpl.ProvideService, wire.Bind(new(tag.Service), new(*tagimpl.Service)), authnimpl.ProvideService, authnimpl.ProvideIdentitySynchronizer, authnimpl.ProvideAuthnService, authnimpl.ProvideAuthnServiceAuthenticateOnly, authnimpl.ProvideRegistration, supportbundlesimpl.ProvideService, extsvcaccounts.ProvideExtSvcAccountsService, wire.Bind(new(serviceaccounts.ExtSvcAccountsService), new(*extsvcaccounts.ExtSvcAccountsService)), registry2.ProvideExtSvcRegistry, wire.Bind(new(extsvcauth.ExternalServiceRegistry), new(*registry2.Registry)), anonstore.ProvideAnonDBStore, wire.Bind(new(anonstore.AnonStore), new(*anonstore.AnonDBStore)), loggermw.Provide, slogadapter.Provide, signingkeysimpl.ProvideEmbeddedSigningKeysService, wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)), ssosettingsimpl.ProvideService, wire.Bind(new(ssosettings.Service), new(*ssosettingsimpl.Service)), idimpl.ProvideService, wire.Bind(new(auth.IDService), new(*idimpl.Service)), cloudmigrationimpl.ProvideService, caching.ProvideCachingServiceClient, userimpl.ProvideVerifier, connectors.ProvideOrgRoleMapper, wire.Bind(new(user.Verifier), new(*userimpl.Verifier)), authz.WireSet, metadata.ProvideSecureValueMetadataStorage, metadata.ProvideKeeperMetadataStorage, metadata.ProvideDecryptStorage, decrypt.ProvideDecryptAuthorizer, wire.Value([]decrypt.ExtraOwnerDecrypter(nil)), decrypt.ProvideDecryptService, inline.ProvideInlineSecureValueService, encryption.ProvideDataKeyStorage, encryption.ProvideGlobalDataKeyStorage, encryption.ProvideEncryptedValueStorage, encryption.ProvideGlobalEncryptedValueStorage, encryption.ProvideEncryptedValueMigrationExecutor, service5.ProvideSecureValueService, validator.ProvideKeeperValidator, validator.ProvideSecureValueValidator, mutator.ProvideKeeperMutator, mutator.ProvideSecureValueMutator, migrator.NewWithEngine, database4.ProvideDatabase, clock.ProvideClock, wire.Bind(new(contracts.Database), new(*database4.Database)), wire.Bind(new(contracts.Clock), new(*clock.Clock)), manager2.ProvideEncryptionManager, service4.ProvideAESGCMCipherService, resource.ProvideStorageMetrics, resource.ProvideIndexMetrics, migrations2.ProvideUnifiedStorageMigrationService, apiserver.WireSet, apiregistry.WireSet, appregistry.WireSet, client.ProvideK8sClientWithFallback) +var wireBasicSet = wire.NewSet(annotationsimpl.ProvideService, wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)), New, api.ProvideHTTPServer, query.ProvideService, wire.Bind(new(query.Service), new(*query.ServiceImpl)), bus.ProvideBus, wire.Bind(new(bus.Bus), new(*bus.InProcBus)), rendering.ProvideService, wire.Bind(new(rendering.Service), new(*rendering.RenderingService)), routing.ProvideRegister, wire.Bind(new(routing.RouteRegister), new(*routing.RouteRegisterImpl)), hooks.ProvideService, kvstore.ProvideService, localcache.ProvideService, bundleregistry.ProvideService, wire.Bind(new(supportbundles.Service), new(*bundleregistry.Service)), updatemanager.ProvideGrafanaService, updatemanager.ProvidePluginsService, service.ProvideService, wire.Bind(new(usagestats.Service), new(*service.UsageStats)), validator3.ProvideService, provisioning.ProvideStubProvisioningService, legacy.ProvideMigratorDashboardAccessor, migrations2.ProvideUnifiedMigrator, pluginsintegration.WireSet, dashboards.ProvideFileStoreManager, wire.Bind(new(dashboards.FileStore), new(*dashboards.FileStoreManager)), cloudwatch.ProvideService, cloudmonitoring.ProvideService, azuremonitor.ProvideService, postgres.ProvideService, mysql.ProvideService, mssql.ProvideService, store.ProvideEntityEventsService, dualwrite.ProvideService, httpclientprovider.New, wire.Bind(new(httpclient.Provider), new(*httpclient2.Provider)), serverlock.ProvideService, wire.Bind(new(installsync.ServerLock), new(*serverlock.ServerLockService)), annotationsimpl.ProvideCleanupService, wire.Bind(new(annotations.Cleaner), new(*annotationsimpl.CleanupServiceImpl)), cleanup.ProvideService, shorturlimpl.ProvideService, wire.Bind(new(shorturls.Service), new(*shorturlimpl.ShortURLService)), queryhistory.ProvideService, wire.Bind(new(queryhistory.Service), new(*queryhistory.QueryHistoryService)), correlations.ProvideService, wire.Bind(new(correlations.Service), new(*correlations.CorrelationsService)), quotaimpl.ProvideService, remotecache.ProvideService, wire.Bind(new(remotecache.CacheStorage), new(*remotecache.RemoteCache)), authinfoimpl.ProvideService, wire.Bind(new(login.AuthInfoService), new(*authinfoimpl.Service)), authinfoimpl.ProvideStore, datasourceproxy.ProvideService, sort.ProvideService, search2.ProvideService, searchV2.ProvideService, searchV2.ProvideSearchHTTPService, store.ProvideService, store.ProvideSystemUsersService, live.ProvideService, live.ProvideDashboardActivityChannel, pushhttp.ProvideService, contexthandler.ProvideService, service12.ProvideService, wire.Bind(new(service12.LDAP), new(*service12.LDAPImpl)), jwt.ProvideService, wire.Bind(new(jwt.JWTService), new(*jwt.AuthService)), store2.ProvideDBStore, image.ProvideDeleteExpiredService, ngalert.ProvideService, librarypanels.ProvideService, wire.Bind(new(librarypanels.Service), new(*librarypanels.LibraryPanelService)), libraryelements.ProvideService, wire.Bind(new(libraryelements.Service), new(*libraryelements.LibraryElementService)), notifications.ProvideService, notifications.ProvideSmtpService, github.ProvideFactory, tracing.ProvideService, tracing.ProvideTracingConfig, wire.Bind(new(tracing.Tracer), new(*tracing.TracingService)), withOTelSet, testdatasource.ProvideService, api4.ProvideService, opentsdb.ProvideService, socialimpl.ProvideService, influxdb.ProvideService, wire.Bind(new(social.Service), new(*socialimpl.SocialService)), tempo.ProvideService, loki.ProvideService, graphite.ProvideService, prometheus.ProvideService, elasticsearch.ProvideService, pyroscope.ProvideService, parca.ProvideService, zipkin.ProvideService, jaeger.ProvideService, service9.ProvideCacheService, wire.Bind(new(datasources.CacheService), new(*service9.CacheServiceImpl)), service2.ProvideEncryptionService, wire.Bind(new(encryption2.Internal), new(*service2.Service)), manager.ProvideSecretsService, wire.Bind(new(secrets.Service), new(*manager.SecretsService)), database.ProvideSecretsStore, wire.Bind(new(secrets.Store), new(*database.SecretsStoreImpl)), garbagecollectionworker.ProvideWorker, grafanads.ProvideService, wire.Bind(new(dashboardsnapshots.Store), new(*database5.DashboardSnapshotStore)), database5.ProvideStore, wire.Bind(new(dashboardsnapshots.Service), new(*service10.ServiceImpl)), service10.ProvideService, service9.ProvideService, wire.Bind(new(datasources.DataSourceService), new(*service9.Service)), service9.ProvideLegacyDataSourceLookup, retriever.ProvideService, wire.Bind(new(serviceaccounts.ServiceAccountRetriever), new(*retriever.Service)), ossaccesscontrol.ProvideServiceAccountPermissions, wire.Bind(new(accesscontrol.ServiceAccountPermissionsService), new(*ossaccesscontrol.ServiceAccountPermissionsService)), manager3.ProvideServiceAccountsService, proxy.ProvideServiceAccountsProxy, wire.Bind(new(serviceaccounts.Service), new(*proxy.ServiceAccountsProxy)), dsquerierclient.NewNullQSDatasourceClientBuilder, expr.ProvideService, featuremgmt.ProvideManagerService, featuremgmt.ProvideToggles, service7.ProvideDashboardServiceImpl, wire.Bind(new(dashboards2.PermissionsRegistrationService), new(*service7.DashboardServiceImpl)), service7.ProvideDashboardService, service7.ProvideDashboardProvisioningService, service7.ProvideDashboardPluginService, database2.ProvideDashboardStore, folderimpl.ProvideService, wire.Bind(new(folder.Service), new(*folderimpl.Service)), wire.Bind(new(folder.LegacyService), new(*folderimpl.Service)), folderimpl.ProvideStore, wire.Bind(new(folder.Store), new(*folderimpl.FolderStoreImpl)), service11.ProvideService, wire.Bind(new(dashboardimport.Service), new(*service11.ImportDashboardService)), service8.ProvideService, wire.Bind(new(plugindashboards.Service), new(*service8.Service)), service8.ProvideDashboardUpdater, kvstore2.ProvideService, avatar.ProvideAvatarCacheServer, statscollector.ProvideService, csrf.ProvideCSRFFilter, wire.Bind(new(csrf.Service), new(*csrf.CSRF)), ossaccesscontrol.ProvideTeamPermissions, wire.Bind(new(accesscontrol.TeamPermissionsService), new(*ossaccesscontrol.TeamPermissionsService)), ossaccesscontrol.ProvideFolderPermissions, wire.Bind(new(accesscontrol.FolderPermissionsService), new(*ossaccesscontrol.FolderPermissionsService)), ossaccesscontrol.ProvideDashboardPermissions, wire.Bind(new(accesscontrol.DashboardPermissionsService), new(*ossaccesscontrol.DashboardPermissionsService)), ossaccesscontrol.ProvideReceiverPermissionsService, wire.Bind(new(accesscontrol.ReceiverPermissionsService), new(*ossaccesscontrol.ReceiverPermissionsService)), starimpl.ProvideService, playlistimpl.ProvideService, apikeyimpl.ProvideService, dashverimpl.ProvideService, service3.ProvideService, wire.Bind(new(publicdashboards.Service), new(*service3.PublicDashboardServiceImpl)), database3.ProvideStore, wire.Bind(new(publicdashboards.Store), new(*database3.PublicDashboardStoreImpl)), metric.ProvideService, api2.ProvideApi, api3.ProvideApi, userimpl.ProvideService, orgimpl.ProvideService, orgimpl.ProvideDeletionService, statsimpl.ProvideService, grpccontext.ProvideContextHandler, grpcserver.ProvideHealthService, grpcserver.ProvideReflectionService, resolver.ProvideEntityReferenceResolver, teamimpl.ProvideService, teamapi.ProvideTeamAPI, tempuserimpl.ProvideService, loginattemptimpl.ProvideService, wire.Bind(new(loginattempt.Service), new(*loginattemptimpl.Service)), migrations3.ProvideDataSourceMigrationService, migrations3.ProvideSecretMigrationProvider, wire.Bind(new(migrations3.SecretMigrationProvider), new(*migrations3.SecretMigrationProviderImpl)), promtypemigration.ProvideAzurePromMigrationService, promtypemigration.ProvideAmazonPromMigrationService, promtypemigration.ProvidePromTypeMigrationProvider, wire.Bind(new(promtypemigration.PromTypeMigrationProvider), new(*promtypemigration.PromTypeMigrationProviderImpl)), resourcepermissions.NewActionSetService, wire.Bind(new(accesscontrol.ActionResolver), new(resourcepermissions.ActionSetService)), wire.Bind(new(pluginaccesscontrol.ActionSetRegistry), new(resourcepermissions.ActionSetService)), permreg.ProvidePermissionRegistry, acimpl.ProvideAccessControl, accesscontrol.ProvideFixedRolesLoader, dualwrite2.ProvideZanzanaReconciler, navtreeimpl.ProvideService, wire.Bind(new(accesscontrol.AccessControl), new(*acimpl.AccessControl)), wire.Bind(new(notifications.TempUserStore), new(tempuser.Service)), tagimpl.ProvideService, wire.Bind(new(tag.Service), new(*tagimpl.Service)), authnimpl.ProvideService, authnimpl.ProvideIdentitySynchronizer, authnimpl.ProvideAuthnService, authnimpl.ProvideAuthnServiceAuthenticateOnly, authnimpl.ProvideRegistration, supportbundlesimpl.ProvideService, extsvcaccounts.ProvideExtSvcAccountsService, wire.Bind(new(serviceaccounts.ExtSvcAccountsService), new(*extsvcaccounts.ExtSvcAccountsService)), registry2.ProvideExtSvcRegistry, wire.Bind(new(extsvcauth.ExternalServiceRegistry), new(*registry2.Registry)), anonstore.ProvideAnonDBStore, wire.Bind(new(anonstore.AnonStore), new(*anonstore.AnonDBStore)), loggermw.Provide, slogadapter.Provide, signingkeysimpl.ProvideEmbeddedSigningKeysService, wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)), ssosettingsimpl.ProvideService, wire.Bind(new(ssosettings.Service), new(*ssosettingsimpl.Service)), idimpl.ProvideService, wire.Bind(new(auth.IDService), new(*idimpl.Service)), cloudmigrationimpl.ProvideService, caching.ProvideCachingServiceClient, userimpl.ProvideVerifier, connectors.ProvideOrgRoleMapper, wire.Bind(new(user.Verifier), new(*userimpl.Verifier)), authz.WireSet, metadata.ProvideSecureValueMetadataStorage, metadata.ProvideKeeperMetadataStorage, metadata.ProvideDecryptStorage, decrypt.ProvideDecryptAuthorizer, wire.Value([]decrypt.ExtraOwnerDecrypter(nil)), decrypt.ProvideDecryptService, inline.ProvideInlineSecureValueService, encryption.ProvideDataKeyStorage, encryption.ProvideGlobalDataKeyStorage, encryption.ProvideEncryptedValueStorage, encryption.ProvideGlobalEncryptedValueStorage, encryption.ProvideEncryptedValueMigrationExecutor, service5.ProvideSecureValueService, validator.ProvideKeeperValidator, validator.ProvideSecureValueValidator, mutator.ProvideKeeperMutator, mutator.ProvideSecureValueMutator, migrator.NewWithEngine, database4.ProvideDatabase, clock.ProvideClock, wire.Bind(new(contracts.Database), new(*database4.Database)), wire.Bind(new(contracts.Clock), new(*clock.Clock)), manager2.ProvideEncryptionManager, service4.ProvideAESGCMCipherService, resource.ProvideStorageMetrics, resource.ProvideIndexMetrics, migrations2.ProvideUnifiedStorageMigrationService, apiserver.WireSet, apiregistry.WireSet, appregistry.WireSet, client.ProvideK8sClientWithFallback) var wireSet = wire.NewSet( wireBasicSet, metrics.WireSet, sqlstore.ProvideService, metrics2.ProvideService, wire.Bind(new(notifications.Service), new(*notifications.NotificationService)), wire.Bind(new(notifications.WebhookSender), new(*notifications.NotificationService)), wire.Bind(new(notifications.EmailSender), new(*notifications.NotificationService)), wire.Bind(new(db.DB), new(*sqlstore.SQLStore)), prefimpl.ProvideService, oauthtoken.ProvideService, wire.Bind(new(oauthtoken.OAuthTokenService), new(*oauthtoken.Service)), wire.Bind(new(cleanup.AlertRuleService), new(*store2.DBstore)), diff --git a/pkg/services/apiserver/appinstaller/installer.go b/pkg/services/apiserver/appinstaller/installer.go index b6e9145ff15..c7b926bd5f3 100644 --- a/pkg/services/apiserver/appinstaller/installer.go +++ b/pkg/services/apiserver/appinstaller/installer.go @@ -108,6 +108,9 @@ func RegisterAuthorizers( if authorizerProvider, ok := installer.(AuthorizerProvider); ok { authorizer := authorizerProvider.GetAuthorizer() for _, gv := range installer.GroupVersions() { + if authorizer == nil { + panic("authorizer cannot be nil for api group: " + gv.String()) + } registrar.Register(gv, authorizer) logger.Debug("Registered authorizer", "group", gv.Group, "version", gv.Version, "app") } diff --git a/pkg/services/apiserver/auth/authorizer/authorizer.go b/pkg/services/apiserver/auth/authorizer/authorizer.go index 54ea8c081d7..f58c1d14bf8 100644 --- a/pkg/services/apiserver/auth/authorizer/authorizer.go +++ b/pkg/services/apiserver/auth/authorizer/authorizer.go @@ -42,7 +42,9 @@ func NewGrafanaBuiltInSTAuthorizer(cfg *setting.Cfg) *GrafanaAuthorizer { // org role is last -- and will return allow for verbs that match expectations // The apiVersion flavors will run first and can return early when FGAC has appropriate rules - authorizers = append(authorizers, newRoleAuthorizer()) + // NOTE: role authorizer is now used by some api groups as their specific authorizer + // but there are still some apis not directly registered in the embedded delegate that benefit from including it here + authorizers = append(authorizers, NewRoleAuthorizer()) return &GrafanaAuthorizer{ apis: apis, auth: union.New(authorizers...), diff --git a/pkg/services/apiserver/auth/authorizer/role.go b/pkg/services/apiserver/auth/authorizer/role.go index 39b3b440665..23164dbf556 100644 --- a/pkg/services/apiserver/auth/authorizer/role.go +++ b/pkg/services/apiserver/auth/authorizer/role.go @@ -19,7 +19,7 @@ var orgRoleNoneAsViewerAPIGroups = []string{ type roleAuthorizer struct{} -func newRoleAuthorizer() *roleAuthorizer { +func NewRoleAuthorizer() *roleAuthorizer { return &roleAuthorizer{} } diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 605016c112d..ac38dee7e3f 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -275,6 +275,8 @@ func (s *service) start(ctx context.Context) error { auth := a.GetAuthorizer() if auth != nil { s.authorizer.Register(gv, auth) + } else { + panic("authorizer can not be nil for api group=" + gv.String()) } } } diff --git a/pkg/services/authz/rbac.go b/pkg/services/authz/rbac.go index 23de10511a2..c6c078a9b4a 100644 --- a/pkg/services/authz/rbac.go +++ b/pkg/services/authz/rbac.go @@ -152,7 +152,7 @@ func ProvideStandaloneAuthZClient( //nolint:staticcheck // not yet migrated to OpenFeature zanzanaEnabled := features.IsEnabledGlobally(featuremgmt.FlagZanzana) - zanzanaClient, err := ProvideStandaloneZanzanaClient(cfg, features) + zanzanaClient, err := ProvideStandaloneZanzanaClient(cfg, features, reg) if err != nil { return nil, err } diff --git a/pkg/services/authz/zanzana.go b/pkg/services/authz/zanzana.go index da77010d0eb..f6751258f2d 100644 --- a/pkg/services/authz/zanzana.go +++ b/pkg/services/authz/zanzana.go @@ -4,16 +4,19 @@ import ( "context" "errors" "fmt" + "time" "github.com/fullstorydev/grpchan/inprocgrpc" authnlib "github.com/grafana/authlib/authn" authzv1 "github.com/grafana/authlib/authz/proto/v1" "github.com/grafana/authlib/grpcutils" "github.com/grafana/authlib/types" + "github.com/grafana/dskit/middleware" "github.com/grafana/dskit/services" grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" openfgav1 "github.com/openfga/api/proto/openfga/v1" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -43,14 +46,14 @@ func ProvideZanzanaClient(cfg *setting.Cfg, db db.DB, tracer tracing.Tracer, fea switch cfg.ZanzanaClient.Mode { case setting.ZanzanaModeClient: - return NewRemoteZanzanaClient( - fmt.Sprintf("stacks-%s", cfg.StackID), - ZanzanaClientConfig{ - URL: cfg.ZanzanaClient.Addr, - Token: cfg.ZanzanaClient.Token, - TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL, - ServerCertFile: cfg.ZanzanaClient.ServerCertFile, - }) + zanzanaConfig := ZanzanaClientConfig{ + Addr: cfg.ZanzanaClient.Addr, + Token: cfg.ZanzanaClient.Token, + TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL, + TokenNamespace: cfg.ZanzanaClient.TokenNamespace, + ServerCertFile: cfg.ZanzanaClient.ServerCertFile, + } + return NewRemoteZanzanaClient(zanzanaConfig, reg) case setting.ZanzanaModeEmbedded: logger := log.New("zanzana.server") @@ -97,32 +100,33 @@ func ProvideZanzanaClient(cfg *setting.Cfg, db db.DB, tracer tracing.Tracer, fea // ProvideStandaloneZanzanaClient provides a standalone Zanzana client, without registering the Zanzana service. // Client connects to a remote Zanzana server specified in the configuration. -func ProvideStandaloneZanzanaClient(cfg *setting.Cfg, features featuremgmt.FeatureToggles) (zanzana.Client, error) { +func ProvideStandaloneZanzanaClient(cfg *setting.Cfg, features featuremgmt.FeatureToggles, reg prometheus.Registerer) (zanzana.Client, error) { //nolint:staticcheck // not yet migrated to OpenFeature if !features.IsEnabledGlobally(featuremgmt.FlagZanzana) { return zClient.NewNoopClient(), nil } zanzanaConfig := ZanzanaClientConfig{ - URL: cfg.ZanzanaClient.Addr, + Addr: cfg.ZanzanaClient.Addr, Token: cfg.ZanzanaClient.Token, TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL, + TokenNamespace: cfg.ZanzanaClient.TokenNamespace, ServerCertFile: cfg.ZanzanaClient.ServerCertFile, } - return NewRemoteZanzanaClient(cfg.ZanzanaClient.TokenNamespace, zanzanaConfig) + return NewRemoteZanzanaClient(zanzanaConfig, reg) } type ZanzanaClientConfig struct { - URL string + Addr string Token string TokenExchangeURL string - ServerCertFile string TokenNamespace string + ServerCertFile string } // NewRemoteZanzanaClient creates a new Zanzana client that connects to remote Zanzana server. -func NewRemoteZanzanaClient(namespace string, cfg ZanzanaClientConfig) (zanzana.Client, error) { +func NewRemoteZanzanaClient(cfg ZanzanaClientConfig, reg prometheus.Registerer) (zanzana.Client, error) { tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{ Token: cfg.Token, TokenExchangeURL: cfg.TokenExchangeURL, @@ -139,18 +143,25 @@ func NewRemoteZanzanaClient(namespace string, cfg ZanzanaClientConfig) (zanzana. } } + authzRequestDuration := promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{ + Name: "authz_zanzana_client_request_duration_seconds", + Help: "Time spent executing requests to zanzana server.", + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 160, + NativeHistogramMinResetDuration: time.Hour, + }, []string{"operation", "status_code"}) + unaryInterceptors, streamInterceptors := instrument(authzRequestDuration, middleware.ReportGRPCStatusOption) + dialOptions := []grpc.DialOption{ grpc.WithTransportCredentials(transportCredentials), grpc.WithPerRPCCredentials( - NewGRPCTokenAuth( - AuthzServiceAudience, - namespace, - tokenClient, - ), + NewGRPCTokenAuth(AuthzServiceAudience, cfg.TokenNamespace, tokenClient), ), + grpc.WithChainUnaryInterceptor(unaryInterceptors...), + grpc.WithChainStreamInterceptor(streamInterceptors...), } - conn, err := grpc.NewClient(cfg.URL, dialOptions...) + conn, err := grpc.NewClient(cfg.Addr, dialOptions...) if err != nil { return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err) } diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index b63b4a40f96..4c3bf3fea2f 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -1305,6 +1305,11 @@ func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query * if err != nil { return nil, err } + + if query.ID <= 0 { + return nil, dashboards.ErrDashboardNotFound + } + result, err := dr.searchDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{ OrgId: requester.GetOrgID(), DashboardIds: []int64{query.ID}, @@ -1316,7 +1321,7 @@ func (dr *DashboardServiceImpl) GetDashboardUIDByID(ctx context.Context, query * if len(result) == 0 { return nil, dashboards.ErrDashboardNotFound } else if len(result) > 1 { - return nil, fmt.Errorf("unexpected number of dashboards found: %d. desired: 1", len(result)) + return nil, fmt.Errorf("unexpected number of dashboards for id %d. found: %d. desired: 1", query.ID, len(result)) } return &dashboards.DashboardRef{UID: result[0].UID, Slug: result[0].Slug, FolderUID: result[0].FolderUID}, nil diff --git a/pkg/services/dashboards/service/dashboard_service_test.go b/pkg/services/dashboards/service/dashboard_service_test.go index 35ac47afd9a..dafb925adb5 100644 --- a/pkg/services/dashboards/service/dashboard_service_test.go +++ b/pkg/services/dashboards/service/dashboard_service_test.go @@ -1663,6 +1663,13 @@ func TestGetDashboardUIDByID(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedResult, result) k8sCliMock.AssertExpectations(t) + + // 0 should return error + _, err = service.GetDashboardUIDByID(ctx, &dashboards.GetDashboardRefByIDQuery{ + ID: 0, + }) + require.Error(t, err) + require.Equal(t, dashboards.ErrDashboardNotFound, err) } func TestUnstructuredToLegacyDashboard(t *testing.T) { diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index feb38de0b4e..c1b318c97b4 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1631,6 +1631,14 @@ var ( Owner: grafanaFrontendSearchNavOrganise, Expression: "false", }, + { + Name: "recentlyViewedDashboards", + Description: "Enables recently viewed dashboards section in the browsing dashboard page", + Stage: FeatureStageExperimental, + Owner: grafanaFrontendSearchNavOrganise, + FrontendOnly: true, + Expression: "false", + }, { Name: "alertEnrichment", Description: "Enable configuration of alert enrichments in Grafana Cloud.", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 3e8324db87a..f05b0d41da3 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -224,6 +224,7 @@ kubernetesAuthzZanzanaSync,experimental,@grafana/identity-access-team,false,fals kubernetesAuthnMutation,experimental,@grafana/identity-access-team,false,false,false kubernetesExternalGroupMapping,experimental,@grafana/identity-access-team,false,false,false restoreDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,false +recentlyViewedDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,true alertEnrichment,experimental,@grafana/alerting-squad,false,false,false alertEnrichmentMultiStep,experimental,@grafana/alerting-squad,false,false,false alertEnrichmentConditional,experimental,@grafana/alerting-squad,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 4dcaf34f924..561ffd562ca 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -2933,6 +2933,23 @@ "requiresRestart": true } }, + { + "metadata": { + "name": "recentlyViewedDashboards", + "resourceVersion": "1765227168175", + "creationTimestamp": "2025-12-08T20:44:44Z", + "annotations": { + "grafana.app/updatedTimestamp": "2025-12-08 20:52:48.175129 +0000 UTC" + } + }, + "spec": { + "description": "Enables recently viewed dashboards section in the browsing dashboard page", + "stage": "experimental", + "codeowner": "@grafana/grafana-search-navigate-organise", + "frontend": true, + "expression": "false" + } + }, { "metadata": { "name": "refactorVariablesTimeRange", diff --git a/pkg/services/live/features/dashboard.go b/pkg/services/live/features/dashboard.go index 286dad7c49c..cda4893c258 100644 --- a/pkg/services/live/features/dashboard.go +++ b/pkg/services/live/features/dashboard.go @@ -22,39 +22,13 @@ const ( ActionDeleted actionType = "deleted" EditingStarted actionType = "editing-started" //EditingFinished actionType = "editing-finished" - - GitopsChannel = "grafana/dashboard/gitops" ) -type userDisplayDTO struct { - ID int64 `json:"id,omitempty"` - UID string `json:"uid,omitempty"` - Name string `json:"name,omitempty"` - Login string `json:"login,omitempty"` - AvatarURL string `json:"avatarUrl"` -} - -// Static function to parse a requester into a userDisplayDTO -func newUserDisplayDTOFromRequester(requester identity.Requester) *userDisplayDTO { - // nolint:staticcheck - userID, _ := requester.GetInternalID() - return &userDisplayDTO{ - ID: userID, - UID: requester.GetRawIdentifier(), - Login: requester.GetLogin(), - Name: requester.GetName(), - } -} - // DashboardEvent events related to dashboards type dashboardEvent struct { - UID string `json:"uid"` - Action actionType `json:"action"` // saved, editing, deleted - User *userDisplayDTO `json:"user,omitempty"` - SessionID string `json:"sessionId,omitempty"` - Message string `json:"message,omitempty"` - Dashboard *dashboards.Dashboard `json:"dashboard,omitempty"` - Error string `json:"error,omitempty"` + UID string `json:"uid"` + Action actionType `json:"action"` // saved, editing, deleted + SessionID string `json:"sessionId,omitempty"` } // DashboardHandler manages all the `grafana/dashboard/*` channels @@ -134,9 +108,6 @@ func (h *DashboardHandler) OnPublish(ctx context.Context, requester identity.Req return model.PublishReply{}, backend.PublishStreamStatusNotFound, nil // NOOP } - // Tell everyone who is editing - event.User = newUserDisplayDTOFromRequester(requester) - msg, err := json.Marshal(event) if err != nil { return model.PublishReply{}, backend.PublishStreamStatusNotFound, fmt.Errorf("internal error") @@ -153,55 +124,21 @@ func (h *DashboardHandler) publish(orgID int64, event dashboardEvent) error { if err != nil { return err } - - // Only broadcast non-error events - if event.Error == "" { - err = h.Publisher(orgID, "grafana/dashboard/uid/"+event.UID, msg) - if err != nil { - return err - } - } - - // Send everything to the gitops channel - return h.Publisher(orgID, GitopsChannel, msg) + return h.Publisher(orgID, "grafana/dashboard/uid/"+event.UID, msg) } // DashboardSaved will broadcast to all connected dashboards -func (h *DashboardHandler) DashboardSaved(orgID int64, requester identity.Requester, message string, dashboard *dashboards.Dashboard, err error) error { - if err != nil && !h.HasGitOpsObserver(orgID) { - return nil // only broadcast if it was OK - } - - msg := dashboardEvent{ - UID: dashboard.UID, - Action: ActionSaved, - User: newUserDisplayDTOFromRequester(requester), - Message: message, - Dashboard: dashboard, - } - - if err != nil { - msg.Error = err.Error() - } - - return h.publish(orgID, msg) -} - -// DashboardDeleted will broadcast to all connected dashboards -func (h *DashboardHandler) DashboardDeleted(orgID int64, requester identity.Requester, uid string) error { +func (h *DashboardHandler) DashboardSaved(orgID int64, uid string) error { return h.publish(orgID, dashboardEvent{ UID: uid, - Action: ActionDeleted, - User: newUserDisplayDTOFromRequester(requester), + Action: ActionSaved, }) } -// HasGitOpsObserver will return true if anyone is listening to the `gitops` channel -func (h *DashboardHandler) HasGitOpsObserver(orgID int64) bool { - count, err := h.ClientCount(orgID, GitopsChannel) - if err != nil { - logger.Error("Error getting client count", "error", err) - return false - } - return count > 0 +// DashboardDeleted will broadcast to all connected dashboards +func (h *DashboardHandler) DashboardDeleted(orgID int64, uid string) error { + return h.publish(orgID, dashboardEvent{ + UID: uid, + Action: ActionDeleted, + }) } diff --git a/pkg/services/live/live.go b/pkg/services/live/live.go index 4d91eae3e5e..cffdaaf0ec6 100644 --- a/pkg/services/live/live.go +++ b/pkg/services/live/live.go @@ -513,18 +513,17 @@ type GrafanaLive struct { // DashboardActivityChannel is a service to advertise dashboard activity type DashboardActivityChannel interface { - // Called when a dashboard is saved -- this includes the error so we can support a - // gitops workflow that knows if the value was saved to the local database or not - // in many cases all direct save requests will fail, but the request should be forwarded - // to any gitops observers - DashboardSaved(orgID int64, requester identity.Requester, message string, dashboard *dashboards.Dashboard, err error) error + // Called when a dashboard is saved + DashboardSaved(orgID int64, uid string) error // Called when a dashboard is deleted - DashboardDeleted(orgID int64, requester identity.Requester, uid string) error + DashboardDeleted(orgID int64, uid string) error +} - // Experimental! Indicate is GitOps is active. This really means - // someone is subscribed to the `grafana/dashboards/gitops` channel - HasGitOpsObserver(orgID int64) bool +// ProvideDashboardActivityChannel extracts the DashboardActivityChannel from GrafanaLive. +// This is used by wire to inject the channel into the dashboard API service. +func ProvideDashboardActivityChannel(live *GrafanaLive) DashboardActivityChannel { + return live.GrafanaScope.Dashboards } func (g *GrafanaLive) getStreamPlugin(ctx context.Context, pluginID string) (backend.StreamHandler, error) { @@ -1178,12 +1177,6 @@ func (g *GrafanaLive) HandleListHTTP(c *contextmodel.ReqContext) response.Respon // HandleInfoHTTP special http response for func (g *GrafanaLive) HandleInfoHTTP(ctx *contextmodel.ReqContext) response.Response { - path := web.Params(ctx.Req)["*"] - if path == "grafana/dashboards/gitops" { - return response.JSON(http.StatusOK, util.DynMap{ - "active": g.GrafanaScope.Dashboards.HasGitOpsObserver(ctx.GetOrgID()), - }) - } return response.JSONStreaming(http.StatusNotFound, util.DynMap{ "message": "Info is not supported for this channel", }) diff --git a/pkg/services/ngalert/metrics/scheduler.go b/pkg/services/ngalert/metrics/scheduler.go index d6c6707f0a9..9bcff59ffb9 100644 --- a/pkg/services/ngalert/metrics/scheduler.go +++ b/pkg/services/ngalert/metrics/scheduler.go @@ -130,7 +130,7 @@ func NewSchedulerMetrics(r prometheus.Registerer) *Scheduler { Name: "rule_group_rules", Help: "The number of alert rules that are scheduled, by type and state.", }, - []string{"org", "type", "state", "rule_group"}, + []string{"org", "type", "state", "rule_group", "folder_uid"}, ), Groups: promauto.With(r).NewGaugeVec( prometheus.GaugeOpts{ diff --git a/pkg/services/ngalert/schedule/alert_rule.go b/pkg/services/ngalert/schedule/alert_rule.go index 3991df89e77..ebb3a1aa1e9 100644 --- a/pkg/services/ngalert/schedule/alert_rule.go +++ b/pkg/services/ngalert/schedule/alert_rule.go @@ -47,10 +47,10 @@ type Rule interface { Identifier() ngmodels.AlertRuleKeyWithGroup } -type ruleFactoryFunc func(context.Context, *ngmodels.AlertRule) Rule +type ruleFactoryFunc func(context.Context, ruleWithFolder) Rule -func (f ruleFactoryFunc) new(ctx context.Context, rule *ngmodels.AlertRule) Rule { - return f(ctx, rule) +func (f ruleFactoryFunc) new(ctx context.Context, rf ruleWithFolder) Rule { + return f(ctx, rf) } func newRuleFactory( @@ -70,11 +70,11 @@ func newRuleFactory( evalAppliedHook evalAppliedFunc, stopAppliedHook stopAppliedFunc, ) ruleFactoryFunc { - return func(ctx context.Context, rule *ngmodels.AlertRule) Rule { - if rule.Type() == ngmodels.RuleTypeRecording { + return func(ctx context.Context, rf ruleWithFolder) Rule { + if rf.rule.Type() == ngmodels.RuleTypeRecording { return newRecordingRule( ctx, - rule.GetKeyWithGroup(), + rf.rule.GetKeyWithGroup(), retryConfig, clock, evalFactory, @@ -89,7 +89,7 @@ func newRuleFactory( } return newAlertRule( ctx, - rule.GetKeyWithGroup(), + rf, appURL, disableGrafanaFolder, retryConfig, @@ -111,7 +111,8 @@ type evalAppliedFunc = func(ngmodels.AlertRuleKey, time.Time) type stopAppliedFunc = func(ngmodels.AlertRuleKey) type alertRule struct { - key ngmodels.AlertRuleKeyWithGroup + key ngmodels.AlertRuleKeyWithGroup + currentFingerprint fingerprint evalCh chan *Evaluation updateCh chan *Evaluation @@ -139,7 +140,7 @@ type alertRule struct { func newAlertRule( parent context.Context, - key ngmodels.AlertRuleKeyWithGroup, + rf ruleWithFolder, appURL *url.URL, disableGrafanaFolder bool, retryConfig RetryConfig, @@ -154,10 +155,13 @@ func newAlertRule( evalAppliedHook func(ngmodels.AlertRuleKey, time.Time), stopAppliedHook func(ngmodels.AlertRuleKey), ) *alertRule { + key := rf.rule.GetKeyWithGroup() + initialFingerprint := rf.Fingerprint() ctx, stop := util.WithCancelCause(ngmodels.WithRuleKey(parent, key.AlertRuleKey)) - return &alertRule{ + a := &alertRule{ key: key, + currentFingerprint: initialFingerprint, evalCh: make(chan *Evaluation), updateCh: make(chan *Evaluation), ctx: ctx, @@ -176,6 +180,8 @@ func newAlertRule( tracer: tracer, featureToggles: featureToggles, } + + return a } func (a *alertRule) Identifier() ngmodels.AlertRuleKeyWithGroup { @@ -246,22 +252,23 @@ func (a *alertRule) Run() error { grafanaCtx := a.ctx a.logger.Debug("Alert rule routine started") - var currentFingerprint fingerprint + firstEvalDone := false + defer a.stopApplied() for { select { // used by external services (API) to notify that rule is updated. case ctx := <-a.updateCh: fp := ctx.Fingerprint() - if currentFingerprint == fp { - a.logger.Info("Rule's fingerprint has not changed. Skip resetting the state", "currentFingerprint", currentFingerprint) + if a.currentFingerprint == fp { + a.logger.Info("Rule's fingerprint has not changed. Skip resetting the state", "currentFingerprint", a.currentFingerprint) continue } a.logger.Info("Clearing the state of the rule because it was updated", "isPaused", ctx.rule.IsPaused, "fingerprint", fp) // clear the state. So the next evaluation will start from the scratch. a.resetState(grafanaCtx, ctx.rule, ctx.rule.IsPaused) - currentFingerprint = fp + a.currentFingerprint = fp // evalCh - used by the scheduler to signal that evaluation is needed. case ctx, ok := <-a.evalCh: if !ok { @@ -295,20 +302,21 @@ func (a *alertRule) Run() error { for { isPaused := ctx.rule.IsPaused - // Do not clean up state if the eval loop has just started. var needReset bool - if currentFingerprint != 0 && currentFingerprint != f { - logger.Debug("Got a new version of alert rule. Clear up the state", "current_fingerprint", currentFingerprint, "fingerprint", f) + if a.currentFingerprint != f { + logger.Debug("Got a new version of alert rule. Clear up the state", "current_fingerprint", a.currentFingerprint, "fingerprint", f) needReset = true } // We need to reset state if the loop has started and the alert is already paused. It can happen, // if we have an alert with state and we do file provision with stateful Grafana, that state // lingers in DB and won't be cleaned up until next alert rule update. - needReset = needReset || (currentFingerprint == 0 && isPaused) + needReset = needReset || (!firstEvalDone && isPaused) if needReset { a.resetState(grafanaCtx, ctx.rule, isPaused) } - currentFingerprint = f + + firstEvalDone = true + a.currentFingerprint = f if isPaused { logger.Debug("Skip rule evaluation because it is paused") return @@ -319,7 +327,7 @@ func (a *alertRule) Run() error { evalTotal.Inc() } - fpStr := currentFingerprint.String() + fpStr := a.currentFingerprint.String() utcTick := ctx.scheduledAt.UTC().Format(time.RFC3339Nano) tracingCtx, span := a.tracer.Start(grafanaCtx, "alert rule execution", trace.WithAttributes( attribute.String("rule_uid", ctx.rule.UID), diff --git a/pkg/services/ngalert/schedule/alert_rule_test.go b/pkg/services/ngalert/schedule/alert_rule_test.go index 48be6bb4a40..6f4144b37bf 100644 --- a/pkg/services/ngalert/schedule/alert_rule_test.go +++ b/pkg/services/ngalert/schedule/alert_rule_test.go @@ -334,7 +334,7 @@ func TestAlertRuleAfterEval(t *testing.T) { ruleStore.PutRule(context.Background(), rule) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) return &testContext{ rule: rule, @@ -503,7 +503,14 @@ func blankRuleForTests(ctx context.Context, key models.AlertRuleKeyWithGroup) *a Log: log.NewNopLogger(), } st := state.NewManager(managerCfg, state.NewNoopPersister()) - return newAlertRule(ctx, key, nil, false, RetryConfig{}, nil, st, nil, nil, nil, log.NewNopLogger(), nil, featuremgmt.WithFeatures(), nil, nil) + // Create a minimal rule from the key + rule := &models.AlertRule{ + OrgID: key.OrgID, + UID: key.UID, + RuleGroup: key.RuleGroup, + } + rf := ruleWithFolder{rule: rule, folderTitle: ""} + return newAlertRule(ctx, rf, nil, false, RetryConfig{}, nil, st, nil, nil, nil, log.NewNopLogger(), nil, featuremgmt.WithFeatures(), nil, nil) } func TestRuleRoutine(t *testing.T) { @@ -540,7 +547,7 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) go func() { _ = ruleInfo.Run() }() @@ -728,7 +735,8 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) - ruleInfo := factory.new(ctx, rule) + folderTitle := "" + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) go func() { err := ruleInfo.Run() stoppedChan <- err @@ -750,7 +758,7 @@ func TestRuleRoutine(t *testing.T) { require.NotEmpty(t, sch.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID)) factory := ruleFactoryFromScheduler(sch) - ruleInfo := factory.new(context.Background(), rule) + ruleInfo := factory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) go func() { err := ruleInfo.Run() stoppedChan <- err @@ -774,7 +782,7 @@ func TestRuleRoutine(t *testing.T) { require.NotEmpty(t, sch.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID)) factory := ruleFactoryFromScheduler(sch) - ruleInfo := factory.new(context.Background(), rule) + ruleInfo := factory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) go func() { err := ruleInfo.Run() stoppedChan <- err @@ -804,7 +812,7 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) go func() { _ = ruleInfo.Run() @@ -871,6 +879,140 @@ func TestRuleRoutine(t *testing.T) { }) }) + t.Run("when update is sent before first evaluation", func(t *testing.T) { + rule := gen.With(withQueryForState(t, eval.Normal)).GenerateRef() + folderTitle := "folderName" + + evalAppliedChan := make(chan time.Time) + + sender := NewSyncAlertsSenderMock() + sender.EXPECT().Send(mock.Anything, rule.GetKey(), mock.Anything).Return() + + sch, ruleStore, _, _ := createSchedule(evalAppliedChan, sender, clock.NewMock()) + ruleStore.PutRule(context.Background(), rule) + sch.schedulableAlertRules.set([]*models.AlertRule{rule}, map[models.FolderKey]string{rule.GetFolderKey(): folderTitle}) + + // Add state to verify it's not cleared + states := []*state.State{ + { + AlertRuleUID: rule.UID, + CacheID: data.Labels(rule.Labels).Fingerprint(), + OrgID: rule.OrgID, + State: eval.Alerting, + StartsAt: sch.clock.Now(), + EndsAt: sch.clock.Now().Add(5 * time.Second), + Labels: rule.Labels, + }, + } + sch.stateManager.Put(states) + + t.Run("should not reset state if fingerprint is the same", func(t *testing.T) { + factory := ruleFactoryFromScheduler(sch) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) + + go func() { + _ = ruleInfo.Run() + }() + + // Send update before first evaluation - same rule, same fingerprint + // This should not reset state since fingerprint is the same + ruleInfo.Update(&Evaluation{rule: rule, folderTitle: folderTitle}) + + // Give time for update to be processed + time.Sleep(100 * time.Millisecond) + + actualStates := sch.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID) + require.NotEmpty(t, actualStates) + }) + + t.Run("should reset state if fingerprint is different", func(t *testing.T) { + // Re-add state for this test + sch.stateManager.Put(states) + + sender := NewSyncAlertsSenderMock() + sender.EXPECT().Send(mock.Anything, rule.GetKey(), mock.Anything).Return() + + sch2, ruleStore2, _, _ := createSchedule(make(chan time.Time), sender, clock.NewMock()) + ruleStore2.PutRule(context.Background(), rule) + sch2.schedulableAlertRules.set([]*models.AlertRule{rule}, map[models.FolderKey]string{rule.GetFolderKey(): folderTitle}) + sch2.stateManager.Put(states) + + factory := ruleFactoryFromScheduler(sch2) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) + + go func() { + _ = ruleInfo.Run() + }() + + // Send update before first eval, with a changed alert rule title. + // This should reset state and send resolved alerts + updatedRule := models.CopyRule(rule, gen.WithTitle(util.GenerateShortUID())) + ruleInfo.Update(&Evaluation{rule: updatedRule, folderTitle: folderTitle}) + + // Wait for sender to be called (which happens when state is cleared and resolved alerts are sent) + require.Eventually(t, func() bool { + return len(sender.Calls()) > 0 + }, 5*time.Second, 100*time.Millisecond) + + // State should be cleared because fingerprint changed + actualStates := sch2.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID) + require.Empty(t, actualStates) + }) + }) + + t.Run("paused rule should reset state on first evaluation", func(t *testing.T) { + rule := gen.With(withQueryForState(t, eval.Normal)).GenerateRef() + rule.IsPaused = true + folderTitle := "folderName" + + sender := NewSyncAlertsSenderMock() + sender.EXPECT().Send(mock.Anything, rule.GetKey(), mock.Anything).Return() + + sch, ruleStore, _, _ := createSchedule(make(chan time.Time), sender, clock.NewMock()) + ruleStore.PutRule(context.Background(), rule) + sch.schedulableAlertRules.set([]*models.AlertRule{rule}, map[models.FolderKey]string{rule.GetFolderKey(): folderTitle}) + + states := []*state.State{ + { + AlertRuleUID: rule.UID, + CacheID: data.Labels(rule.Labels).Fingerprint(), + OrgID: rule.OrgID, + State: eval.Alerting, + StartsAt: sch.clock.Now(), + EndsAt: sch.clock.Now().Add(5 * time.Second), + Labels: rule.Labels, + }, + } + sch.stateManager.Put(states) + require.NotEmpty(t, sch.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID)) + + factory := ruleFactoryFromScheduler(sch) + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: folderTitle}) + + go func() { + _ = ruleInfo.Run() + }() + + ruleInfo.Eval(&Evaluation{ + scheduledAt: sch.clock.Now(), + rule: rule, + folderTitle: folderTitle, + }) + + require.Eventually(t, func() bool { + return len(sender.Calls()) > 0 + }, 5*time.Second, 100*time.Millisecond) + + actualStates := sch.stateManager.GetStatesForRuleUID(rule.OrgID, rule.UID) + require.Empty(t, actualStates) + }) + t.Run("when evaluation fails", func(t *testing.T) { rule := gen.With(withQueryForState(t, eval.Error)).GenerateRef() rule.ExecErrState = models.ErrorErrState @@ -910,7 +1052,7 @@ func TestRuleRoutine(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: ""}) go func() { _ = ruleInfo.Run() @@ -1044,7 +1186,7 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: ""}) go func() { _ = ruleInfo.Run() @@ -1078,7 +1220,7 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: ""}) go func() { _ = ruleInfo.Run() @@ -1119,7 +1261,7 @@ func TestRuleRoutine(t *testing.T) { factory := ruleFactoryFromScheduler(sch) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: ""}) go func() { _ = ruleInfo.Run() @@ -1214,7 +1356,7 @@ func TestAlertRuleRetry(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - ruleInfo := factory.new(ctx, rule) + ruleInfo := factory.new(ctx, ruleWithFolder{rule: rule, folderTitle: ""}) go func() { _ = ruleInfo.Run() diff --git a/pkg/services/ngalert/schedule/metrics.go b/pkg/services/ngalert/schedule/metrics.go index e3281c33710..f4630b39cad 100644 --- a/pkg/services/ngalert/schedule/metrics.go +++ b/pkg/services/ngalert/schedule/metrics.go @@ -117,7 +117,7 @@ func (sch *schedule) updateRulesMetrics(alertRules []*models.AlertRule) { // Set metrics for key, count := range buckets { - sch.metrics.GroupRules.WithLabelValues(fmt.Sprint(key.orgID), key.ruleType.String(), key.state, makeRuleGroupLabelValue(key.ruleGroup)).Set(float64(count)) + sch.metrics.GroupRules.WithLabelValues(fmt.Sprint(key.orgID), key.ruleType.String(), key.state, makeRuleGroupLabelValue(key.ruleGroup), key.ruleGroup.NamespaceUID).Set(float64(count)) } for orgID, numRulesNfSettings := range orgsNfSettings { sch.metrics.SimpleNotificationRules.WithLabelValues(fmt.Sprint(orgID)).Set(float64(numRulesNfSettings)) diff --git a/pkg/services/ngalert/schedule/recording_rule_test.go b/pkg/services/ngalert/schedule/recording_rule_test.go index e8a61979d19..da25c38a2df 100644 --- a/pkg/services/ngalert/schedule/recording_rule_test.go +++ b/pkg/services/ngalert/schedule/recording_rule_test.go @@ -239,7 +239,7 @@ func TestRecordingRuleAfterEval(t *testing.T) { ruleStore.PutRule(context.Background(), rule) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) evalDoneChan := make(chan time.Time, 1) // Buffer to avoid blocking afterEvalCh := make(chan struct{}, 1) // Buffer to avoid blocking @@ -444,7 +444,7 @@ func testRecordingRule_Integration(t *testing.T, writeTarget *writer.TestRemoteW folderTitle := ruleStore.getNamespaceTitle(rule.NamespaceUID) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) evalDoneChan := make(chan time.Time) process.(*recordingRule).evalAppliedHook = func(_ models.AlertRuleKey, t time.Time) { evalDoneChan <- t @@ -583,7 +583,7 @@ func testRecordingRule_Integration(t *testing.T, writeTarget *writer.TestRemoteW folderTitle := ruleStore.getNamespaceTitle(rule.NamespaceUID) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) evalDoneChan := make(chan time.Time) process.(*recordingRule).evalAppliedHook = func(_ models.AlertRuleKey, t time.Time) { evalDoneChan <- t @@ -728,7 +728,7 @@ func testRecordingRule_Integration(t *testing.T, writeTarget *writer.TestRemoteW folderTitle := ruleStore.getNamespaceTitle(rule.NamespaceUID) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) evalDoneChan := make(chan time.Time) process.(*recordingRule).evalAppliedHook = func(_ models.AlertRuleKey, t time.Time) { evalDoneChan <- t @@ -787,7 +787,7 @@ func testRecordingRule_Integration(t *testing.T, writeTarget *writer.TestRemoteW folderTitle := ruleStore.getNamespaceTitle(rule.NamespaceUID) ruleFactory := ruleFactoryFromScheduler(sch) - process := ruleFactory.new(context.Background(), rule) + process := ruleFactory.new(context.Background(), ruleWithFolder{rule: rule, folderTitle: ""}) evalDoneChan := make(chan time.Time) process.(*recordingRule).evalAppliedHook = func(_ models.AlertRuleKey, t time.Time) { evalDoneChan <- t diff --git a/pkg/services/ngalert/schedule/registry.go b/pkg/services/ngalert/schedule/registry.go index ede71c94d75..892e0af8235 100644 --- a/pkg/services/ngalert/schedule/registry.go +++ b/pkg/services/ngalert/schedule/registry.go @@ -21,7 +21,7 @@ var ( ) type ruleFactory interface { - new(context.Context, *models.AlertRule) Rule + new(context.Context, ruleWithFolder) Rule } type ruleRegistry struct { @@ -35,14 +35,14 @@ func newRuleRegistry() ruleRegistry { // getOrCreate gets a rule routine from registry for the provided rule. If it does not exist, it creates a new one. // Returns a pointer to the rule routine and a flag that indicates whether it is a new struct or not. -func (r *ruleRegistry) getOrCreate(context context.Context, item *models.AlertRule, factory ruleFactory) (Rule, bool) { +func (r *ruleRegistry) getOrCreate(context context.Context, rf ruleWithFolder, factory ruleFactory) (Rule, bool) { r.mu.Lock() defer r.mu.Unlock() - key := item.GetKey() + key := rf.rule.GetKey() rule, ok := r.rules[key] if !ok { - rule = factory.new(context, item) + rule = factory.new(context, rf) r.rules[key] = rule } return rule, !ok diff --git a/pkg/services/ngalert/schedule/schedule.go b/pkg/services/ngalert/schedule/schedule.go index 44dced0cdea..ba12fe33461 100644 --- a/pkg/services/ngalert/schedule/schedule.go +++ b/pkg/services/ngalert/schedule/schedule.go @@ -320,10 +320,22 @@ func (sch *schedule) processTick(ctx context.Context, dispatcherGroup *errgroup. sch.stopAppliedFunc, ) for _, item := range alertRules { - ruleRoutine, newRoutine := sch.registry.getOrCreate(ctx, item, ruleFactory) key := item.GetKey() logger := sch.log.FromContext(ctx).New(key.LogContext()...) + var folderTitle string + if !sch.disableGrafanaFolder { + title, ok := folderTitles[item.GetFolderKey()] + if ok { + folderTitle = title + } else { + missingFolder[item.NamespaceUID] = append(missingFolder[item.NamespaceUID], item.UID) + } + } + + rf := ruleWithFolder{rule: item, folderTitle: folderTitle} + ruleRoutine, newRoutine := sch.registry.getOrCreate(ctx, rf, ruleFactory) + // enforce minimum evaluation interval if item.IntervalSeconds < int64(sch.minRuleInterval.Seconds()) { logger.Debug("Interval adjusted", "originalInterval", item.IntervalSeconds, "adjustedInterval", sch.minRuleInterval.Seconds()) @@ -337,7 +349,7 @@ func (sch *schedule) processTick(ctx context.Context, dispatcherGroup *errgroup. logger.Debug("Rule restarted because type changed", "old", ruleRoutine.Type(), "new", item.Type()) restartedRules = append(restartedRules, ruleRoutine) sch.registry.del(key) - ruleRoutine, newRoutine = sch.registry.getOrCreate(ctx, item, ruleFactory) + ruleRoutine, newRoutine = sch.registry.getOrCreate(ctx, rf, ruleFactory) } if newRoutine && !invalidInterval { @@ -357,16 +369,6 @@ func (sch *schedule) processTick(ctx context.Context, dispatcherGroup *errgroup. offset := jitterOffsetInTicks(item, sch.baseInterval, sch.jitterEvaluations) isReadyToRun := item.IntervalSeconds != 0 && (tickNum%itemFrequency)-offset == 0 - var folderTitle string - if !sch.disableGrafanaFolder { - title, ok := folderTitles[item.GetFolderKey()] - if ok { - folderTitle = title - } else { - missingFolder[item.NamespaceUID] = append(missingFolder[item.NamespaceUID], item.UID) - } - } - if isReadyToRun { logger.Debug("Rule is ready to run on the current tick", "tick", tick, "frequency", itemFrequency, "offset", offset) readyToRun = append(readyToRun, readyToRunItem{ruleRoutine: ruleRoutine, Evaluation: Evaluation{ @@ -378,12 +380,12 @@ func (sch *schedule) processTick(ctx context.Context, dispatcherGroup *errgroup. if _, isUpdated := updated[key]; isUpdated && !isReadyToRun { // if we do not need to eval the rule, check the whether rule was just updated and if it was, notify evaluation routine about that logger.Debug("Rule has been updated. Notifying evaluation routine") - go func(routine Rule, rule *ngmodels.AlertRule) { + go func(routine Rule, rule *ngmodels.AlertRule, folder string) { routine.Update(&Evaluation{ rule: rule, - folderTitle: folderTitle, + folderTitle: folder, }) - }(ruleRoutine, item) + }(ruleRoutine, item, folderTitle) updatedRules = append(updatedRules, ngmodels.AlertRuleKeyWithVersion{ Version: item.Version, AlertRuleKey: item.GetKey(), diff --git a/pkg/services/ngalert/schedule/schedule_unit_test.go b/pkg/services/ngalert/schedule/schedule_unit_test.go index c90c9b19707..bbfa20fceb1 100644 --- a/pkg/services/ngalert/schedule/schedule_unit_test.go +++ b/pkg/services/ngalert/schedule/schedule_unit_test.go @@ -157,8 +157,8 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1) + grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, alertRule1.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -199,9 +199,9 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -265,9 +265,9 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -308,10 +308,10 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="paused",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="paused",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="paused",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) }) @@ -336,9 +336,9 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -363,8 +363,8 @@ func TestProcessTicks(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup2, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) }) @@ -684,8 +684,8 @@ func TestSchedule_updateRulesMetrics(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active", type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1) + grafana_alerting_rule_group_rules{folder_uid="%[3]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, alertRule1.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -705,9 +705,9 @@ func TestSchedule_updateRulesMetrics(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -720,9 +720,9 @@ func TestSchedule_updateRulesMetrics(t *testing.T) { expectedMetric := fmt.Sprintf( `# HELP grafana_alerting_rule_group_rules The number of alert rules that are scheduled, by type and state. # TYPE grafana_alerting_rule_group_rules gauge - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 - grafana_alerting_rule_group_rules{org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 - `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2) + grafana_alerting_rule_group_rules{folder_uid="%[4]s",org="%[1]d",rule_group="%[2]s",state="active",type="alerting"} 1 + grafana_alerting_rule_group_rules{folder_uid="%[5]s",org="%[1]d",rule_group="%[3]s",state="active",type="alerting"} 1 + `, alertRule1.OrgID, folderWithRuleGroup1, folderWithRuleGroup2, alertRule1.NamespaceUID, alertRule2.NamespaceUID) err := testutil.GatherAndCompare(reg, bytes.NewBufferString(expectedMetric), "grafana_alerting_rule_group_rules") require.NoError(t, err) @@ -1107,7 +1107,7 @@ func TestSchedule_deleteAlertRule(t *testing.T) { rule := models.RuleGen.GenerateRef() ruleStore.PutRule(ctx, rule) key := rule.GetKey() - info, _ := sch.registry.getOrCreate(ctx, rule, ruleFactory) + info, _ := sch.registry.getOrCreate(ctx, ruleWithFolder{rule: rule, folderTitle: ""}, ruleFactory) sch.deleteAlertRule(ctx, key) @@ -1126,7 +1126,7 @@ func TestSchedule_deleteAlertRule(t *testing.T) { rule := models.RuleGen.GenerateRef() ruleStore.PutRule(ctx, rule) key := rule.GetKey() - info, _ := sch.registry.getOrCreate(ctx, rule, ruleFactory) + info, _ := sch.registry.getOrCreate(ctx, ruleWithFolder{rule: rule, folderTitle: ""}, ruleFactory) _, err := sch.updateSchedulableAlertRules(ctx) require.NoError(t, err) @@ -1149,7 +1149,7 @@ func TestSchedule_deleteAlertRule(t *testing.T) { rule := models.RuleGen.GenerateRef() ruleStore.PutRule(ctx, rule) key := rule.GetKey() - info, _ := sch.registry.getOrCreate(ctx, rule, ruleFactory) + info, _ := sch.registry.getOrCreate(ctx, ruleWithFolder{rule: rule, folderTitle: ""}, ruleFactory) _, err := sch.updateSchedulableAlertRules(ctx) require.NoError(t, err) @@ -1172,7 +1172,7 @@ func TestSchedule_deleteAlertRule(t *testing.T) { ruleFactory := ruleFactoryFromScheduler(sch) rule := models.RuleGen.GenerateRef() key := rule.GetKey() - info, _ := sch.registry.getOrCreate(ctx, rule, ruleFactory) + info, _ := sch.registry.getOrCreate(ctx, ruleWithFolder{rule: rule, folderTitle: ""}, ruleFactory) _, err := sch.updateSchedulableAlertRules(ctx) require.NoError(t, err) diff --git a/pkg/services/updatemanager/plugins.go b/pkg/services/updatemanager/plugins.go index 79270f8a1ee..7ee11b261f9 100644 --- a/pkg/services/updatemanager/plugins.go +++ b/pkg/services/updatemanager/plugins.go @@ -13,6 +13,7 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/open-feature/go-sdk/openfeature" "go.opentelemetry.io/otel/codes" "github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider" @@ -96,11 +97,7 @@ func (s *PluginsService) IsDisabled() bool { } func (s *PluginsService) Run(ctx context.Context) error { - s.instrumentedCheckForUpdates(ctx) - //nolint:staticcheck // not yet migrated to OpenFeature - if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) { - s.updateAll(ctx) - } + s.checkAndUpdate(ctx) ticker := time.NewTicker(time.Minute * 10) run := true @@ -108,11 +105,7 @@ func (s *PluginsService) Run(ctx context.Context) error { for run { select { case <-ticker.C: - s.instrumentedCheckForUpdates(ctx) - //nolint:staticcheck // not yet migrated to OpenFeature - if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) { - s.updateAll(ctx) - } + s.checkAndUpdate(ctx) case <-ctx.Done(): run = false } @@ -140,6 +133,14 @@ func (s *PluginsService) HasUpdate(ctx context.Context, pluginID string) (string return "", false } +// checkAndUpdate checks for updates and applies them if auto-update is enabled. +func (s *PluginsService) checkAndUpdate(ctx context.Context) { + s.instrumentedCheckForUpdates(ctx) + if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) { + s.updateAll(ctx) + } +} + func (s *PluginsService) instrumentedCheckForUpdates(ctx context.Context) { start := time.Now() ctx, span := s.tracer.Start(ctx, "updatechecker.PluginsService.checkForUpdates") @@ -226,8 +227,7 @@ func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugi return false } - //nolint:staticcheck // not yet migrated to OpenFeature - if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) { + if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) { return s.updateChecker.CanUpdate(plugin.ID, plugin.Info.Version, gcomVersion, s.updateStrategy == setting.PluginUpdateStrategyMinor) } diff --git a/pkg/services/updatemanager/plugins_test.go b/pkg/services/updatemanager/plugins_test.go index 0455963f168..75834c44c7d 100644 --- a/pkg/services/updatemanager/plugins_test.go +++ b/pkg/services/updatemanager/plugins_test.go @@ -6,8 +6,10 @@ import ( "net/http" "net/url" "strings" + "sync" "testing" + "github.com/open-feature/go-sdk/openfeature" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/log" @@ -19,6 +21,7 @@ import ( "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker" "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore" "github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins" + "github.com/grafana/grafana/pkg/setting" ) type mockPluginPreinstall struct { @@ -240,6 +243,7 @@ func TestPluginUpdateChecker_checkForUpdates(t *testing.T) { require.Empty(t, svc.availableUpdates["test-core-panel"]) }) } + func TestPluginUpdateChecker_updateAll(t *testing.T) { t.Run("update is available", func(t *testing.T) { pluginsFakeStore := map[string]string{} @@ -300,3 +304,88 @@ func (c *fakeHTTPClient) Do(req *http.Request) (*http.Response, error) { return resp, nil } + +func TestPluginsService_PluginsAutoUpdateFlag(t *testing.T) { + updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck") + + tests := []struct { + name string + flagEnabled bool + expectUpdate bool + }{ + { + name: "pluginsAutoUpdate enabled calls updateAll", + flagEnabled: true, + expectUpdate: true, + }, + { + name: "pluginsAutoUpdate disabled does not call updateAll", + flagEnabled: false, + expectUpdate: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + setupOpenFeatureProvider(t, tt.flagEnabled) + + updateCallCount := 0 + + availableUpdates := map[string]availableUpdate{ + "test-plugin": { + localVersion: "0.9.0", + availableVersion: "1.0.0", + }, + } + + svc := &PluginsService{ + availableUpdates: availableUpdates, + httpClient: &fakeHTTPClient{ + fakeResp: `[]`, + }, + log: log.NewNopLogger(), + tracer: tracing.InitializeTracerForTest(), + updateCheckURL: updateCheckURL, + updateChecker: pluginchecker.ProvideService(managedplugins.NewNoop(), provisionedplugins.NewNoop(), &mockPluginPreinstall{}), + pluginStore: &pluginstore.FakePluginStore{PluginList: []pluginstore.Plugin{}}, + pluginInstaller: &pluginfakes.FakePluginInstaller{ + AddFunc: func(ctx context.Context, pluginID, version string, opts plugins.AddOpts) error { + updateCallCount++ + return nil + }, + }, + grafanaVersion: "10.0.0", + } + + ctx := context.Background() + // Test the synchronous initialization work directly, without the long-running ticker loop + svc.checkAndUpdate(ctx) + + if tt.expectUpdate { + require.Equal(t, updateCallCount, 1, "updateAll should be called when flag is enabled") + } else { + require.Equal(t, 0, updateCallCount, "updateAll should not be called when flag is disabled") + } + }) + } +} + +var openfeatureTestMutex sync.Mutex + +func setupOpenFeatureProvider(t *testing.T, flagValue bool) { + t.Helper() + openfeatureTestMutex.Lock() + + err := featuremgmt.InitOpenFeature(featuremgmt.OpenFeatureConfig{ + ProviderType: setting.StaticProviderType, + StaticFlags: map[string]bool{ + featuremgmt.FlagPluginsAutoUpdate: flagValue, + }, + }) + require.NoError(t, err) + + t.Cleanup(func() { + _ = openfeature.SetProviderAndWait(openfeature.NoopProvider{}) + openfeatureTestMutex.Unlock() + }) +} diff --git a/pkg/setting/settings_zanzana.go b/pkg/setting/settings_zanzana.go index 1a06ea034ef..d30304e4bd0 100644 --- a/pkg/setting/settings_zanzana.go +++ b/pkg/setting/settings_zanzana.go @@ -110,15 +110,24 @@ func (cfg *Cfg) readZanzanaSettings() { zc.Mode = "embedded" } - zc.Token = clientSec.Key("token").MustString("") - zc.TokenExchangeURL = clientSec.Key("token_exchange_url").MustString("") zc.Addr = clientSec.Key("address").MustString("") zc.ServerCertFile = clientSec.Key("tls_cert").MustString("") - // TODO: read Token and TokenExchangeURL from grpc_client_authentication section grpcClientAuthSection := cfg.SectionWithEnvOverrides("grpc_client_authentication") + zc.Token = grpcClientAuthSection.Key("token").MustString("") + zc.TokenExchangeURL = grpcClientAuthSection.Key("token_exchange_url").MustString("") zc.TokenNamespace = grpcClientAuthSection.Key("token_namespace").MustString("stacks-" + cfg.StackID) + // TODO: remove old settings when migrated + token := clientSec.Key("token").MustString("") + tokenExchangeURL := clientSec.Key("token_exchange_url").MustString("") + if token != "" { + zc.Token = token + } + if tokenExchangeURL != "" { + zc.TokenExchangeURL = tokenExchangeURL + } + cfg.ZanzanaClient = zc zs := ZanzanaServerSettings{} diff --git a/pkg/storage/unified/resource/fieldSelector.go b/pkg/storage/unified/resource/fieldSelector.go new file mode 100644 index 00000000000..034085ca07f --- /dev/null +++ b/pkg/storage/unified/resource/fieldSelector.go @@ -0,0 +1,53 @@ +package resource + +import ( + "context" + + "github.com/grafana/grafana/pkg/storage/unified/resourcepb" +) + +// Some list queries can be calculated with simple reads or search index +func (s *server) tryFieldSelector(ctx context.Context, req *resourcepb.ListRequest) *resourcepb.ListResponse { + if req.Source != resourcepb.ListRequest_STORE || req.Options.Key.Namespace == "" { + return nil + } + + var names []string + for _, v := range req.Options.Fields { + if v.Key == "metadata.name" && v.Operator == `=` { + names = v.Values + continue + } + + // TODO: support other field selectors + } + + // The required names + if len(names) > 0 { + read := &resourcepb.ReadRequest{ + Key: req.Options.Key, + ResourceVersion: req.ResourceVersion, + } + rsp := &resourcepb.ListResponse{ + ResourceVersion: 1, // TODO, search result should include when it was indexed + } + for _, name := range names { + read.Key.Name = name + found, err := s.Read(ctx, read) + if err != nil { + return &resourcepb.ListResponse{Error: AsErrorResult(err)} + } + if len(found.Value) > 0 { + rsp.Items = append(rsp.Items, &resourcepb.ResourceWrapper{ + Value: found.Value, + ResourceVersion: found.ResourceVersion, + }) + if found.ResourceVersion > rsp.ResourceVersion { + rsp.ResourceVersion = found.ResourceVersion + } + } + } + return rsp + } + return nil +} diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index c4052938603..7c890e72b00 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -1039,7 +1039,7 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour } // Fast path for getting single value in a list - if rsp := s.tryFastPathList(ctx, req); rsp != nil { + if rsp := s.tryFieldSelector(ctx, req); rsp != nil { return rsp, nil } @@ -1137,40 +1137,6 @@ func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resour return rsp, err } -// Some list queries can be calculated with simple reads -func (s *server) tryFastPathList(ctx context.Context, req *resourcepb.ListRequest) *resourcepb.ListResponse { - if req.Source != resourcepb.ListRequest_STORE || req.Options.Key.Namespace == "" { - return nil - } - - for _, v := range req.Options.Fields { - if v.Key == "metadata.name" && v.Operator == `=` { - if len(v.Values) == 1 { - read := &resourcepb.ReadRequest{ - Key: req.Options.Key, - ResourceVersion: req.ResourceVersion, - } - read.Key.Name = v.Values[0] - found, err := s.Read(ctx, read) - if err != nil { - return &resourcepb.ListResponse{Error: AsErrorResult(err)} - } - - // Return a value when it exists - rsp := &resourcepb.ListResponse{} - if len(found.Value) > 0 { - rsp.Items = []*resourcepb.ResourceWrapper{{ - Value: found.Value, - ResourceVersion: found.ResourceVersion, - }} - } - return rsp - } - } - } - return nil -} - // isTrashItemAuthorized checks if the user has access to the trash item. func (s *server) isTrashItemAuthorized(ctx context.Context, iter ListIterator, trashChecker claims.ItemChecker) bool { user, ok := claims.AuthInfoFrom(ctx) diff --git a/pkg/storage/unified/sql/db/migrations/resource_mig.go b/pkg/storage/unified/sql/db/migrations/resource_mig.go index c4db9b1dbb9..fbbfe32d4a6 100644 --- a/pkg/storage/unified/sql/db/migrations/resource_mig.go +++ b/pkg/storage/unified/sql/db/migrations/resource_mig.go @@ -198,5 +198,11 @@ func initResourceTables(mg *migrator.Migrator) string { } mg.AddMigration("create table "+resource_events_table.Name, migrator.NewAddTableMigration(resource_events_table)) + mg.AddMigration("Add IDX_resource_history_key_path index", migrator.NewAddIndexMigration(resource_history_table, &migrator.Index{ + Cols: []string{"key_path"}, + Type: migrator.IndexType, + Name: "IDX_resource_history_key_path", + })) + return marker } diff --git a/pkg/tests/apis/collections/stars_test.go b/pkg/tests/apis/collections/stars_test.go index fe841fb4851..573da05a00b 100644 --- a/pkg/tests/apis/collections/stars_test.go +++ b/pkg/tests/apis/collections/stars_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "slices" "testing" "github.com/stretchr/testify/require" @@ -160,7 +161,7 @@ func TestIntegrationStars(t *testing.T) { { "group": "dashboard.grafana.app", "kind": "Dashboard", - "names": []string{"test-2", "aaa", "bbb"}, + "names": []string{"test-2", "aaa", "aaa", "bbb"}, }, }, }, @@ -174,13 +175,17 @@ func TestIntegrationStars(t *testing.T) { require.Equal(t, "dashboard.grafana.app", resources[0].Group) require.Equal(t, "Dashboard", resources[0].Kind) require.ElementsMatch(t, - []string{"aaa", "bbb", "test-2"}, // NOTE 2 stays, 3 removed, added aaa+bbb (and sorted!) + []string{"test-2", "aaa", "bbb"}, // keeps the requested order, removing duplicates resources[0].Names) rspObj, err = starsClient.Resource.Get(ctx, "user-"+starsClient.Args.User.Identity.GetIdentifier(), metav1.GetOptions{}) require.NoError(t, err) after = typed(t, rspObj, &collections.Stars{}) + + // FIXME: when we remove legacy support this should not sort! + slices.Sort(after.Spec.Resource[0].Names) + jj, err := json.MarshalIndent(after.Spec, "", " ") require.NoError(t, err) require.JSONEq(t, `{ diff --git a/pkg/tests/apis/folder/folders_test.go b/pkg/tests/apis/folder/folders_test.go index 43db6ff0932..1cc58c4bdbd 100644 --- a/pkg/tests/apis/folder/folders_test.go +++ b/pkg/tests/apis/folder/folders_test.go @@ -1879,6 +1879,14 @@ func TestIntegrationMoveNestedFolderToRootK8S(t *testing.T) { require.Equal(t, http.StatusOK, get.Response.StatusCode) require.Equal(t, "f2", get.Result.UID) require.Equal(t, "", get.Result.ParentUID) + + // Check that we can get the same folder using metadata.name selector + results, err := client.Resource.List(context.Background(), metav1.ListOptions{ + FieldSelector: "metadata.name=" + get.Result.UID, + }) + require.NoError(t, err) + require.Len(t, results.Items, 1) + require.Equal(t, "f2", results.Items[0].GetName()) } // Test deleting nested folders ensures postorder deletion diff --git a/public/app/core/icons/cached.json b/public/app/core/icons/cached.json index e78419e6d72..6e35e64dd0c 100644 --- a/public/app/core/icons/cached.json +++ b/public/app/core/icons/cached.json @@ -193,5 +193,6 @@ "unicons/ban", "unicons/git", "unicons/bitbucket", - "unicons/tachometer-fast" + "unicons/tachometer-fast", + "unicons/tachometer-empty" ] diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditPaneSplitter.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditPaneSplitter.tsx index 091e4805bae..e32dbcd090d 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditPaneSplitter.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditPaneSplitter.tsx @@ -191,11 +191,6 @@ function getStyles(theme: GrafanaTheme2, headerHeight: number) { // Because the edit pane splitter handle area adds padding we can reduce it here paddingRight: theme.spacing(1), }), - editPane: css({ - flexDirection: 'column', - // borderLeft: `1px solid ${theme.colors.border.weak}`, - // background: theme.colors.background.primary, - }), controlsWrapperSticky: css({ [theme.breakpoints.up('md')]: { position: 'sticky', diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx index 9e7ea7c8357..27da5cbbb9a 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx @@ -102,7 +102,14 @@ function SaveDashboardDrawerComponent({ model }: SceneComponentProps; + return ( + + ); } if (saveAsCopy || changeInfo.isNew) { diff --git a/public/app/features/dashboard-scene/scene/DashboardScene.tsx b/public/app/features/dashboard-scene/scene/DashboardScene.tsx index a542dd92826..d0fafd6f2bb 100644 --- a/public/app/features/dashboard-scene/scene/DashboardScene.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardScene.tsx @@ -835,7 +835,7 @@ export class DashboardScene extends SceneObjectBase impleme kind: 'Dashboard', metadata: { ...meta.k8s, - name: meta.uid ?? meta.k8s?.name, + name: options.isNew ? undefined : (meta.uid ?? meta.k8s?.name), generateName: options.isNew ? 'd' : undefined, }, spec, diff --git a/public/app/features/dashboard-scene/scene/PanelNonApplicableDrilldownsSubHeader.tsx b/public/app/features/dashboard-scene/scene/PanelNonApplicableDrilldownsSubHeader.tsx index fe8437f9dc5..2f04d91fe90 100644 --- a/public/app/features/dashboard-scene/scene/PanelNonApplicableDrilldownsSubHeader.tsx +++ b/public/app/features/dashboard-scene/scene/PanelNonApplicableDrilldownsSubHeader.tsx @@ -79,16 +79,8 @@ export function PanelNonApplicableDrilldownsSubHeader({ filtersVar, groupByVar, }; fetchApplicability(); - }, [ - filterKey, - groupByKey, - filtersVar, - groupByVar, - queryRunner, - filtersState?.filters, - groupByState?.value, - filtersState?.originFilters, - ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filterKey, groupByKey, filtersVar, groupByVar, queryRunner]); useLayoutEffect(() => { if (!nonApplicable.length) { diff --git a/public/app/features/dashboard-scene/scene/new-toolbar/actions/EditDashboardSwitch.tsx b/public/app/features/dashboard-scene/scene/new-toolbar/actions/EditDashboardSwitch.tsx index 8389e11dfe4..777c3892860 100644 --- a/public/app/features/dashboard-scene/scene/new-toolbar/actions/EditDashboardSwitch.tsx +++ b/public/app/features/dashboard-scene/scene/new-toolbar/actions/EditDashboardSwitch.tsx @@ -1,22 +1,26 @@ import { selectors } from '@grafana/e2e-selectors'; import { t } from '@grafana/i18n'; +import { Button } from '@grafana/ui'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; import { trackDashboardSceneEditButtonClicked } from 'app/features/dashboard-scene/utils/tracking'; import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; import { ToolbarActionProps } from '../types'; -import { ToolbarSwitch } from './ToolbarSwitch'; - export const EditDashboardSwitch = ({ dashboard }: ToolbarActionProps) => { + const tooltip = dashboard.state.isEditing + ? t('dashboard.toolbar.edit-button.exit-tooltip', 'Exit edit mode') + : t('dashboard.toolbar.edit-button.enter-tooltip', 'Enter edit mode'); + + if (playlistSrv.state.isPlaying) { + return null; + } + return ( - { evt.preventDefault(); evt.stopPropagation(); @@ -29,6 +33,10 @@ export const EditDashboardSwitch = ({ dashboard }: ToolbarActionProps) => { dashboard.exitEditMode({ skipConfirm: false }); } }} - /> + > + {dashboard.state.isEditing + ? t('dashboard.toolbar.edit-button.exit', 'Exit edit') + : t('dashboard.toolbar.edit-button.enter', 'Edit')} + ); }; diff --git a/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareDashboardButton.tsx b/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareDashboardButton.tsx index 66eec343ea3..2bb4aa0d7ee 100644 --- a/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareDashboardButton.tsx +++ b/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareDashboardButton.tsx @@ -34,7 +34,7 @@ export const ShareDashboardButton = ({ dashboard }: ToolbarActionProps) => { arrowLabel={t('dashboard.toolbar.new.share.arrow', 'Share')} arrowTestId={newShareButtonSelector.arrowMenu} dashboard={dashboard} - variant={!dashboard.state.isEditing ? 'primary' : 'secondary'} + variant={!dashboard.state.isEditing ? 'primary' : 'canvas'} /> ); }; diff --git a/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareExportDashboardButton.tsx b/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareExportDashboardButton.tsx index 8783cd89751..ad9d28c11a0 100644 --- a/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareExportDashboardButton.tsx +++ b/public/app/features/dashboard-scene/scene/new-toolbar/actions/ShareExportDashboardButton.tsx @@ -1,7 +1,7 @@ import { ReactElement, useState } from 'react'; import { t } from '@grafana/i18n'; -import { Button, ButtonGroup, Dropdown } from '@grafana/ui'; +import { ButtonGroup, Dropdown, ToolbarButton } from '@grafana/ui'; import { appEvents } from 'app/core/app_events'; import { ShowConfirmModalEvent } from 'app/types/events'; @@ -17,7 +17,7 @@ interface Props extends ToolbarActionProps { onButtonClick?: () => void; arrowLabel: string; arrowTestId: string; - variant?: 'primary' | 'secondary'; + variant?: 'primary' | 'canvas'; } export const ShareExportDashboardButton = ({ @@ -31,7 +31,7 @@ export const ShareExportDashboardButton = ({ onButtonClick, arrowLabel, arrowTestId, - variant = 'secondary', + variant = 'canvas', }: Props) => { const [isOpen, setIsOpen] = useState(false); @@ -60,9 +60,15 @@ export const ShareExportDashboardButton = ({ } }} > - + - - - ); -}; - -const getStyles = (theme: GrafanaTheme2) => ({ - container: css({ - border: `1px solid ${theme.components.input.borderColor}`, - padding: theme.spacing(0.5), - backgroundColor: theme.components.input.background, - borderRadius: theme.shape.radius.default, - width: theme.spacing(6.5), - height: theme.spacing(theme.components.height.md), - cursor: 'pointer', - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - - [theme.transitions.handleMotion('no-preference', 'reduce')]: { - transition: 'all 0.2s ease-in-out', - }, - - '&:hover': { - borderColor: theme.components.input.borderHover, - }, - }), - containerChecked: css({ - backgroundColor: theme.colors.primary.main, - borderColor: 'transparent', - - '&:hover': { - backgroundColor: theme.colors.primary.shade, - borderColor: 'transparent', - }, - }), - containerDisabled: css({ - cursor: 'initial', - background: theme.colors.action.disabledBackground, - borderColor: theme.colors.border.weak, - }), - box: css({ - background: theme.colors.background.primary, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - width: theme.spacing(3.5), - height: '100%', - transform: 'translateX(0)', - position: 'relative', - borderRadius: styleMixins.getInternalRadius(theme, 2), - border: `1px solid ${theme.colors.border.weak}`, - - [theme.transitions.handleMotion('no-preference', 'reduce')]: { - transition: 'all 0.2s ease-in-out', - }, - }), - boxChecked: css({ - transform: `translateX(calc(100% - 14px))`, - borderColor: 'transparent', - }), -}); diff --git a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts index dda09ade2d4..cede8f605a5 100644 --- a/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts +++ b/public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts @@ -178,7 +178,7 @@ export class V1DashboardSerializer uid: '', title: options.title || '', description: options.description || undefined, - tags: options.isNew || options.copyTags ? saveModel.tags : [], + tags: options.copyTags === false ? [] : saveModel.tags, }; } @@ -390,7 +390,7 @@ export class V2DashboardSerializer ...saveModel, title: options.title || '', description: options.description || '', - tags: options.isNew || options.copyTags ? saveModel.tags : [], + tags: options.copyTags === false ? [] : saveModel.tags, }; } diff --git a/public/app/features/live/dashboard/types.ts b/public/app/features/live/dashboard/types.ts index 447fc2c9101..cffe686fc27 100644 --- a/public/app/features/live/dashboard/types.ts +++ b/public/app/features/live/dashboard/types.ts @@ -8,7 +8,6 @@ export enum DashboardEventAction { export interface DashboardEvent { uid: string; action: DashboardEventAction; - userId?: number; message?: string; sessionId?: string; timestamp?: number; diff --git a/public/app/features/panel/components/VizTypePicker/VisualizationSuggestions.tsx b/public/app/features/panel/components/VizTypePicker/VisualizationSuggestions.tsx index e60e91bcd96..269f564d833 100644 --- a/public/app/features/panel/components/VizTypePicker/VisualizationSuggestions.tsx +++ b/public/app/features/panel/components/VizTypePicker/VisualizationSuggestions.tsx @@ -30,7 +30,17 @@ export interface Props { export function VisualizationSuggestions({ onChange, data, panel }: Props) { const styles = useStyles2(getStyles); - const { value: suggestions, loading, error } = useAsync(() => getAllSuggestions(data), [data]); + const { + value: suggestions, + loading, + error, + } = useAsync(async () => { + if (!hasData(data)) { + return []; + } + + return await getAllSuggestions(data); + }, [data]); const [suggestionHash, setSuggestionHash] = useState(null); const [firstCardRef, { width }] = useMeasure(); const [firstCardHash, setFirstCardHash] = useState(null); @@ -89,7 +99,7 @@ export function VisualizationSuggestions({ onChange, data, panel }: Props) { } }, [suggestions, suggestionHash, firstCardHash, isNewVizSuggestionsEnabled, isUnconfiguredPanel, applySuggestion]); - if (loading) { + if (loading || !data) { return (
@@ -120,10 +130,6 @@ export function VisualizationSuggestions({ onChange, data, panel }: Props) { ); } - if (!data) { - return null; - } - return (
{isNewVizSuggestionsEnabled diff --git a/public/app/features/panel/suggestions/getAllSuggestions.ts b/public/app/features/panel/suggestions/getAllSuggestions.ts index ef478468cde..ea94add39ab 100644 --- a/public/app/features/panel/suggestions/getAllSuggestions.ts +++ b/public/app/features/panel/suggestions/getAllSuggestions.ts @@ -17,35 +17,31 @@ import { panelsToCheckFirst } from './consts'; /** * gather and cache the plugins which provide visualization suggestions so they can be invoked to build suggestions */ -let _pluginCache: PanelPlugin[] | null = null; async function getPanelsWithSuggestions(): Promise { - if (!_pluginCache) { - _pluginCache = []; + // list of plugins to load is determined by the feature flag + const pluginIds: string[] = config.featureToggles.externalVizSuggestions + ? getAllPanelPluginMeta() + .filter((panel) => panel.suggestions) + .map((m) => m.id) + : panelsToCheckFirst; - // list of plugins to load is determined by the feature flag - const pluginIds: string[] = config.featureToggles.externalVizSuggestions - ? getAllPanelPluginMeta() - .filter((panel) => panel.suggestions) - .map((m) => m.id) - : panelsToCheckFirst; + // import the plugins in parallel using Promise.allSettled + const plugins: PanelPlugin[] = []; + const settledPromises = await Promise.allSettled(pluginIds.map((id) => importPanelPlugin(id))); + for (let i = 0; i < settledPromises.length; i++) { + const settled = settledPromises[i]; - // import the plugins in parallel using Promise.allSettled - const settledPromises = await Promise.allSettled(pluginIds.map((id) => importPanelPlugin(id))); - for (let i = 0; i < settledPromises.length; i++) { - const settled = settledPromises[i]; - - if (settled.status === 'fulfilled') { - _pluginCache.push(settled.value); - } - // TODO: do we want to somehow log if there were errors loading some of the plugins? + if (settled.status === 'fulfilled') { + plugins.push(settled.value); } + // TODO: do we want to somehow log if there were errors loading some of the plugins? } - if (_pluginCache.length === 0) { + if (plugins.length === 0) { throw new Error('No panel plugins with visualization suggestions found'); } - return _pluginCache; + return plugins; } /** @@ -83,7 +79,7 @@ export function sortSuggestions(suggestions: PanelPluginVisualizationSuggestion[ if (mappedA && dataSummary.hasPreferredVisualisationType(mappedA)) { return -1; } - const mappedB = mapPreferredVisualisationTypeToPlugin(a.pluginId); + const mappedB = mapPreferredVisualisationTypeToPlugin(b.pluginId); if (mappedB && dataSummary.hasPreferredVisualisationType(mappedB)) { return 1; } @@ -101,9 +97,8 @@ export function sortSuggestions(suggestions: PanelPluginVisualizationSuggestion[ export async function getAllSuggestions(data?: PanelData): Promise { const dataSummary = getPanelDataSummary(data?.series); const list: PanelPluginVisualizationSuggestion[] = []; - const plugins = await getPanelsWithSuggestions(); - for (const plugin of plugins) { + for (const plugin of await getPanelsWithSuggestions()) { const suggestions = plugin.getSuggestions(dataSummary); if (suggestions) { list.push(...suggestions); diff --git a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboard.tsx b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboard.tsx index fca774ef84c..5c3679ad795 100644 --- a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboard.tsx +++ b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboard.tsx @@ -10,10 +10,14 @@ export interface SaveProvisionedDashboardProps { dashboard: DashboardScene; drawer: SaveDashboardDrawer; changeInfo: DashboardChangeInfo; + saveAsCopy?: boolean; } -export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: SaveProvisionedDashboardProps) { - const { isNew, defaultValues, workflowOptions, readOnly, repository } = useProvisionedDashboardData(dashboard); +export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard, saveAsCopy }: SaveProvisionedDashboardProps) { + const { isNew, defaultValues, workflowOptions, readOnly, repository } = useProvisionedDashboardData( + dashboard, + saveAsCopy + ); if (!defaultValues) { return null; @@ -24,11 +28,12 @@ export function SaveProvisionedDashboard({ drawer, changeInfo, dashboard }: Save dashboard={dashboard} drawer={drawer} changeInfo={changeInfo} - isNew={isNew} + isNew={isNew || !!saveAsCopy} defaultValues={defaultValues} repository={repository} workflowOptions={workflowOptions} readOnly={readOnly} + saveAsCopy={saveAsCopy} /> ); } diff --git a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx index effa2e5c0e7..219bb521111 100644 --- a/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx +++ b/public/app/features/provisioning/components/Dashboards/SaveProvisionedDashboardForm.tsx @@ -6,7 +6,7 @@ import { AppEvents, locationUtil } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { getAppEvents, locationService, reportInteraction } from '@grafana/runtime'; import { Dashboard } from '@grafana/schema'; -import { Button, Field, Input, Stack, TextArea } from '@grafana/ui'; +import { Button, Field, Input, Stack, TextArea, Switch } from '@grafana/ui'; import { RepositoryView, Unstructured } from 'app/api/clients/provisioning/v0alpha1'; import kbn from 'app/core/utils/kbn'; import { Resource } from 'app/features/apiserver/types'; @@ -47,6 +47,7 @@ export function SaveProvisionedDashboardForm({ workflowOptions, readOnly, repository, + saveAsCopy, }: Props) { const navigate = useNavigate(); const appEvents = getAppEvents(); @@ -166,7 +167,15 @@ export function SaveProvisionedDashboardForm({ }); // Submit handler for saving the form data - const handleFormSubmit = async ({ title, description, repo, path, comment, ref }: ProvisionedDashboardFormData) => { + const handleFormSubmit = async ({ + title, + description, + repo, + path, + comment, + ref, + copyTags, + }: ProvisionedDashboardFormData) => { // Validate required fields if (!repo || !path) { console.error('Missing required fields for saving:', { repo, path }); @@ -185,7 +194,8 @@ export function SaveProvisionedDashboardForm({ isNew, title, description, - copyTags: true, + copyTags, + saveAsCopy, }); reportInteraction('grafana_provisioning_dashboard_save_submitted', { @@ -287,6 +297,12 @@ export function SaveProvisionedDashboardForm({ isNew={isNew} /> + {saveAsCopy && ( + + + + )} +