Dashboard Migrations: V30 value mappings and tooltip options (#107970)

* Dashboard Migrations: V30 value mappings and tooltip options
pull/108088/head^2
Ivan Ortega Alba 5 days ago committed by GitHub
parent b839090de5
commit 1200b684c6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      apps/dashboard/pkg/migration/schemaversion/migrations.go
  2. 393
      apps/dashboard/pkg/migration/schemaversion/v30.go
  3. 499
      apps/dashboard/pkg/migration/schemaversion/v30_test.go
  4. 214
      apps/dashboard/pkg/migration/testdata/input/v30.value_mappings_and_tooltip_options.json
  5. 373
      apps/dashboard/pkg/migration/testdata/output/v30.value_mappings_and_tooltip_options.json

@ -5,7 +5,7 @@ import (
)
const (
MIN_VERSION = 30
MIN_VERSION = 29
LATEST_VERSION = 41
)
@ -26,6 +26,7 @@ type DataSourceInfoProvider interface {
func GetMigrations(dsInfoProvider DataSourceInfoProvider) map[int]SchemaVersionMigrationFunc {
return map[int]SchemaVersionMigrationFunc{
30: V30,
31: V31,
32: V32,
33: V33(dsInfoProvider),

@ -0,0 +1,393 @@
package schemaversion
import (
"strconv"
)
// V30 upgrades value mappings and migrates tooltip options for panels.
//
// This migration addresses two key improvements to panel configurations:
// 1. Value mappings upgrade: Converts legacy value mapping format to the new structured format
// 2. Tooltip options migration: Renames tooltipOptions to tooltip in specific panel types: timeseries, xychart, xychart2
//
// The migration works by:
// 1. Iterating through all panels in the dashboard, including nested panels in collapsed rows
// 2. For each panel, upgrading value mappings in fieldConfig.defaults and fieldConfig.overrides
// 3. Migrating tooltip options for timeseries, xychart, and xychart2 panels
// 4. Preserving all other panel configurations and options
//
// Value Mappings Migration:
// - Converts legacy mapping types (1 = ValueToText, 2 = RangeToText) to new format
// - Handles special "null" values by converting them to SpecialValue mappings
// - Preserves threshold colors when available
// - Consolidates multiple value mappings into a single ValueToText mapping
// - Maintains range mappings as separate RangeToText mappings
//
// Tooltip Options Migration:
// - Renames panel.options.tooltipOptions to panel.options.tooltip
// - Only applies to timeseries, xychart, and xychart2 panel types
// - Preserves all tooltip configuration options
//
// Example value mappings transformation:
//
// Before migration:
//
// panel: {
// "fieldConfig": {
// "defaults": {
// "mappings": [
// { "id": 0, "text": "Up", "type": 1, "value": "1" },
// { "id": 1, "text": "Down", "type": 1, "value": "0" },
// { "from": "10", "to": "20", "text": "Medium", "type": 2 }
// ]
// }
// }
// }
//
// After migration:
//
// panel: {
// "fieldConfig": {
// "defaults": {
// "mappings": [
// {
// "type": "value",
// "options": {
// "1": { "text": "Up" },
// "0": { "text": "Down" }
// }
// },
// {
// "type": "range",
// "options": {
// "from": 10,
// "to": 20,
// "result": { "text": "Medium" }
// }
// }
// ]
// }
// }
// }
//
// Example tooltip options transformation:
//
// Before migration:
//
// panel: {
// "type": "timeseries",
// "options": {
// "tooltipOptions": { "mode": "multi" }
// }
// }
//
// After migration:
//
// panel: {
// "type": "timeseries",
// "options": {
// "tooltip": { "mode": "multi" }
// }
// }
func V30(dashboard map[string]interface{}) error {
dashboard["schemaVersion"] = 30
panels, ok := dashboard["panels"].([]interface{})
if !ok {
return nil
}
for _, panel := range panels {
panelMap, ok := panel.(map[string]interface{})
if !ok {
continue
}
upgradeValueMappingsForPanel(panelMap)
migrateTooltipOptions(panelMap)
// Handle nested panels in collapsed rows
nestedPanels, hasNested := panelMap["panels"].([]interface{})
if !hasNested {
continue
}
for _, nestedPanel := range nestedPanels {
nestedPanelMap, ok := nestedPanel.(map[string]interface{})
if !ok {
continue
}
upgradeValueMappingsForPanel(nestedPanelMap)
migrateTooltipOptions(nestedPanelMap)
}
}
return nil
}
// upgradeValueMappingsForPanel migrates value mappings from old format to new format
func upgradeValueMappingsForPanel(panel map[string]interface{}) {
fieldConfig, ok := panel["fieldConfig"].(map[string]interface{})
if !ok {
return
}
// Upgrade defaults mappings
if defaults, ok := fieldConfig["defaults"].(map[string]interface{}); ok {
if mappings, ok := defaults["mappings"].([]interface{}); ok {
var thresholds map[string]interface{}
if t, ok := defaults["thresholds"].(map[string]interface{}); ok {
thresholds = t
}
defaults["mappings"] = upgradeValueMappings(mappings, thresholds)
}
}
// Upgrade overrides mappings
overrides, hasOverrides := fieldConfig["overrides"].([]interface{})
if !hasOverrides {
return
}
for _, override := range overrides {
overrideMap, ok := override.(map[string]interface{})
if !ok {
continue
}
properties, hasProperties := overrideMap["properties"].([]interface{})
if !hasProperties {
continue
}
for _, property := range properties {
propertyMap, ok := property.(map[string]interface{})
if !ok {
continue
}
if propertyMap["id"] != "mappings" {
continue
}
mappings, ok := propertyMap["value"].([]interface{})
if !ok {
continue
}
propertyMap["value"] = upgradeValueMappings(mappings, nil)
}
}
}
// upgradeValueMappings converts legacy value mappings to new format
func upgradeValueMappings(oldMappings []interface{}, thresholds map[string]interface{}) []interface{} {
if len(oldMappings) == 0 {
return oldMappings
}
valueMaps := map[string]interface{}{
"type": "value",
"options": map[string]interface{}{},
}
var newMappings []interface{}
hasValueMappings := false
for _, mapping := range oldMappings {
if mappingMap, ok := mapping.(map[string]interface{}); ok {
// Check if this is already the new format
if mappingType, ok := mappingMap["type"].(string); ok && mappingType != "" {
if mappingType == "value" {
// Consolidate existing value mappings
if options, ok := mappingMap["options"].(map[string]interface{}); ok {
valueMapsOptions := valueMaps["options"].(map[string]interface{})
for k, v := range options {
valueMapsOptions[k] = v
}
hasValueMappings = true
}
} else {
newMappings = append(newMappings, mappingMap)
}
continue
}
// Handle legacy format
var color interface{}
if thresholds != nil {
// Try to get color from threshold based on the mapping value
if value, ok := mappingMap["value"]; ok {
if valueStr, ok := value.(string); ok {
if numeric, err := strconv.ParseFloat(valueStr, 64); err == nil {
color = getActiveThresholdColor(numeric, thresholds)
}
}
}
// For range mappings, use the 'from' value to determine color
if fromVal, ok := mappingMap["from"]; ok {
if fromStr, ok := fromVal.(string); ok {
if numeric, err := strconv.ParseFloat(fromStr, 64); err == nil {
color = getActiveThresholdColor(numeric, thresholds)
}
}
}
}
// Convert legacy type numbers to new format
if mappingType, ok := mappingMap["type"].(float64); ok {
switch int(mappingType) {
case 1: // ValueToText
if value, ok := mappingMap["value"]; ok {
if valueStr, ok := value.(string); ok && valueStr == "null" {
// Handle null values as special value mapping
// For null values, use the base threshold color (lowest step)
if thresholds != nil {
color = getBaseThresholdColor(thresholds)
}
newMappings = append(newMappings, map[string]interface{}{
"type": "special",
"options": map[string]interface{}{
"match": "null",
"result": map[string]interface{}{
"text": mappingMap["text"],
"color": color,
},
},
})
} else {
// Regular value mapping
valueMapsOptions := valueMaps["options"].(map[string]interface{})
result := map[string]interface{}{
"text": mappingMap["text"],
}
if color != nil {
result["color"] = color
}
valueMapsOptions[stringifyValue(value)] = result
hasValueMappings = true
}
}
case 2: // RangeToText
result := map[string]interface{}{
"text": mappingMap["text"],
}
if color != nil {
result["color"] = color
}
from, _ := strconv.ParseFloat(stringifyValue(mappingMap["from"]), 64)
to, _ := strconv.ParseFloat(stringifyValue(mappingMap["to"]), 64)
newMappings = append(newMappings, map[string]interface{}{
"type": "range",
"options": map[string]interface{}{
"from": from,
"to": to,
"result": result,
},
})
}
}
}
}
// Add consolidated value mappings at the beginning if any exist
if hasValueMappings {
newMappings = append([]interface{}{valueMaps}, newMappings...)
}
return newMappings
}
// getActiveThresholdColor returns the color for a value based on thresholds
func getActiveThresholdColor(value float64, thresholds map[string]interface{}) interface{} {
if steps, ok := thresholds["steps"].([]interface{}); ok {
var activeStep map[string]interface{}
for _, step := range steps {
if stepMap, ok := step.(map[string]interface{}); ok {
if stepValue, ok := stepMap["value"]; ok {
if stepValue == nil {
// Null value represents negative infinity - this is always the base color
activeStep = stepMap
continue
}
if stepNum, ok := stepValue.(float64); ok {
if value >= stepNum {
activeStep = stepMap
}
}
}
}
}
if activeStep != nil {
return activeStep["color"]
}
}
return nil
}
// getBaseThresholdColor returns the base threshold color (first step with null value)
func getBaseThresholdColor(thresholds map[string]interface{}) interface{} {
if steps, ok := thresholds["steps"].([]interface{}); ok {
for _, step := range steps {
if stepMap, ok := step.(map[string]interface{}); ok {
if stepValue, ok := stepMap["value"]; ok {
if stepValue == nil {
// This is the base color (null value step)
return stepMap["color"]
}
}
}
}
}
return nil
}
// migrateTooltipOptions renames tooltipOptions to tooltip for specific panel types
func migrateTooltipOptions(panel map[string]interface{}) {
panelType, ok := panel["type"].(string)
if !ok {
return
}
// Only migrate for specific panel types
if panelType != "timeseries" && panelType != "xychart" && panelType != "xychart2" {
return
}
options, ok := panel["options"].(map[string]interface{})
if !ok {
return
}
tooltipOptions, ok := options["tooltipOptions"]
if !ok {
return
}
// Rename tooltipOptions to tooltip
options["tooltip"] = tooltipOptions
delete(options, "tooltipOptions")
}
// stringifyValue converts a value to string
func stringifyValue(value interface{}) string {
switch v := value.(type) {
case string:
return v
case float64:
return strconv.FormatFloat(v, 'f', -1, 64)
case int:
return strconv.Itoa(v)
case bool:
return strconv.FormatBool(v)
default:
return ""
}
}

@ -0,0 +1,499 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
func TestV30(t *testing.T) {
tests := []migrationTestCase{
{
name: "panel with legacy value mappings gets upgraded to new format",
input: map[string]interface{}{
"title": "V30 Value Mappings Migration Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with legacy value mappings",
"id": 1,
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"thresholds": map[string]interface{}{
"mode": "absolute",
"steps": []interface{}{
map[string]interface{}{
"color": "green",
"value": nil,
},
map[string]interface{}{
"color": "red",
"value": float64(80),
},
},
},
"mappings": []interface{}{
map[string]interface{}{
"id": 0,
"text": "Up",
"type": float64(1),
"value": "1",
},
map[string]interface{}{
"id": 1,
"text": "Down",
"type": float64(1),
"value": "0",
},
map[string]interface{}{
"from": "10",
"to": "20",
"text": "Medium",
"type": float64(2),
},
map[string]interface{}{
"type": float64(1),
"value": "null",
"text": "Null Value",
},
},
},
"overrides": []interface{}{
map[string]interface{}{
"matcher": map[string]interface{}{
"id": "byName",
"options": "test-field",
},
"properties": []interface{}{
map[string]interface{}{
"id": "mappings",
"value": []interface{}{
map[string]interface{}{
"id": 0,
"text": "Override Up",
"type": float64(1),
"value": "1",
},
},
},
},
},
},
},
},
},
},
expected: map[string]interface{}{
"title": "V30 Value Mappings Migration Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with legacy value mappings",
"id": 1,
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"thresholds": map[string]interface{}{
"mode": "absolute",
"steps": []interface{}{
map[string]interface{}{
"color": "green",
"value": nil,
},
map[string]interface{}{
"color": "red",
"value": float64(80),
},
},
},
"mappings": []interface{}{
map[string]interface{}{
"type": "value",
"options": map[string]interface{}{
"1": map[string]interface{}{
"text": "Up",
"color": "green",
},
"0": map[string]interface{}{
"text": "Down",
"color": "green",
},
},
},
map[string]interface{}{
"type": "range",
"options": map[string]interface{}{
"from": float64(10),
"to": float64(20),
"result": map[string]interface{}{
"text": "Medium",
"color": "green",
},
},
},
map[string]interface{}{
"type": "special",
"options": map[string]interface{}{
"match": "null",
"result": map[string]interface{}{
"text": "Null Value",
"color": "green",
},
},
},
},
},
"overrides": []interface{}{
map[string]interface{}{
"matcher": map[string]interface{}{
"id": "byName",
"options": "test-field",
},
"properties": []interface{}{
map[string]interface{}{
"id": "mappings",
"value": []interface{}{
map[string]interface{}{
"type": "value",
"options": map[string]interface{}{
"1": map[string]interface{}{
"text": "Override Up",
},
},
},
},
},
},
},
},
},
},
},
},
},
{
name: "panel with tooltip options gets migrated to tooltip",
input: map[string]interface{}{
"title": "V30 Tooltip Options Migration Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with tooltipOptions",
"id": 1,
"options": map[string]interface{}{
"tooltipOptions": map[string]interface{}{
"mode": "multi",
},
},
},
map[string]interface{}{
"type": "xychart",
"title": "XY Chart with tooltipOptions",
"id": 2,
"options": map[string]interface{}{
"tooltipOptions": map[string]interface{}{
"mode": "single",
},
},
},
map[string]interface{}{
"type": "xychart2",
"title": "XY Chart2 with tooltipOptions",
"id": 3,
"options": map[string]interface{}{
"tooltipOptions": map[string]interface{}{
"mode": "single",
},
},
},
},
},
expected: map[string]interface{}{
"title": "V30 Tooltip Options Migration Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with tooltipOptions",
"id": 1,
"options": map[string]interface{}{
"tooltip": map[string]interface{}{
"mode": "multi",
},
},
},
map[string]interface{}{
"type": "xychart",
"title": "XY Chart with tooltipOptions",
"id": 2,
"options": map[string]interface{}{
"tooltip": map[string]interface{}{
"mode": "single",
},
},
},
map[string]interface{}{
"type": "xychart2",
"title": "XY Chart2 with tooltipOptions",
"id": 3,
"options": map[string]interface{}{
"tooltip": map[string]interface{}{
"mode": "single",
},
},
},
},
},
},
{
name: "panel with nested panels in collapsed row gets migrated",
input: map[string]interface{}{
"title": "V30 Nested Panels Migration Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "row",
"title": "Collapsed Row",
"id": 1,
"collapsed": true,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Nested panel with tooltipOptions",
"id": 2,
"options": map[string]interface{}{
"tooltipOptions": map[string]interface{}{
"mode": "multi",
},
},
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"mappings": []interface{}{
map[string]interface{}{
"id": 0,
"text": "On",
"type": float64(1),
"value": "1",
},
},
},
},
},
},
},
},
},
expected: map[string]interface{}{
"title": "V30 Nested Panels Migration Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "row",
"title": "Collapsed Row",
"id": 1,
"collapsed": true,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Nested panel with tooltipOptions",
"id": 2,
"options": map[string]interface{}{
"tooltip": map[string]interface{}{
"mode": "multi",
},
},
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"mappings": []interface{}{
map[string]interface{}{
"type": "value",
"options": map[string]interface{}{
"1": map[string]interface{}{
"text": "On",
},
},
},
},
},
},
},
},
},
},
},
},
{
name: "panel with no mappings or tooltip options remains unchanged",
input: map[string]interface{}{
"title": "V30 No Changes Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with no relevant configurations",
"id": 1,
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"unit": "bytes",
},
},
"options": map[string]interface{}{
"legend": map[string]interface{}{
"displayMode": "list",
},
},
},
},
},
expected: map[string]interface{}{
"title": "V30 No Changes Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "timeseries",
"title": "Panel with no relevant configurations",
"id": 1,
"fieldConfig": map[string]interface{}{
"defaults": map[string]interface{}{
"unit": "bytes",
},
},
"options": map[string]interface{}{
"legend": map[string]interface{}{
"displayMode": "list",
},
},
},
},
},
},
{
name: "panels remain unchanged when no V30 specific migrations apply",
input: map[string]interface{}{
"title": "V30 Panel Types Unchanged Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "graph",
"title": "Graph panel",
"id": 1,
},
map[string]interface{}{
"type": "singlestat",
"title": "Singlestat panel",
"id": 2,
},
map[string]interface{}{
"type": "timeseries",
"title": "Already timeseries panel",
"id": 3,
},
},
},
expected: map[string]interface{}{
"title": "V30 Panel Types Unchanged Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "graph",
"title": "Graph panel",
"id": 1,
},
map[string]interface{}{
"type": "singlestat",
"title": "Singlestat panel",
"id": 2,
},
map[string]interface{}{
"type": "timeseries",
"title": "Already timeseries panel",
"id": 3,
},
},
},
},
{
name: "graph panels with different configurations remain unchanged in V30",
input: map[string]interface{}{
"title": "V30 Graph Panel Configurations Test Dashboard",
"schemaVersion": 29,
"panels": []interface{}{
map[string]interface{}{
"type": "graph",
"title": "Graph with series mode and legend values",
"id": 1,
"xaxis": map[string]interface{}{
"mode": "series",
},
"legend": map[string]interface{}{
"values": true,
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with series mode",
"id": 2,
"xaxis": map[string]interface{}{
"mode": "series",
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with histogram mode",
"id": 3,
"xaxis": map[string]interface{}{
"mode": "histogram",
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with default configuration",
"id": 4,
},
},
},
expected: map[string]interface{}{
"title": "V30 Graph Panel Configurations Test Dashboard",
"schemaVersion": 30,
"panels": []interface{}{
map[string]interface{}{
"type": "graph",
"title": "Graph with series mode and legend values",
"id": 1,
"xaxis": map[string]interface{}{
"mode": "series",
},
"legend": map[string]interface{}{
"values": true,
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with series mode",
"id": 2,
"xaxis": map[string]interface{}{
"mode": "series",
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with histogram mode",
"id": 3,
"xaxis": map[string]interface{}{
"mode": "histogram",
},
},
map[string]interface{}{
"type": "graph",
"title": "Graph with default configuration",
"id": 4,
},
},
},
},
}
runMigrationTests(t, tests, schemaversion.V30)
}

@ -0,0 +1,214 @@
{
"title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard",
"schemaVersion": 29,
"panels": [
{
"type": "timeseries",
"title": "Panel with legacy value mappings and tooltip options",
"id": 1,
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"mappings": [
{
"id": 0,
"text": "Up",
"type": 1,
"value": "1"
},
{
"id": 1,
"text": "Down",
"type": 1,
"value": "0"
},
{
"from": "10",
"to": "20",
"text": "Medium",
"type": 2
},
{
"type": 1,
"value": "null",
"text": "Null Value"
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "test-field"
},
"properties": [
{
"id": "mappings",
"value": [
{
"id": 0,
"text": "Override Up",
"type": 1,
"value": "1"
}
]
}
]
}
]
},
"options": {
"tooltipOptions": {
"mode": "multi"
}
}
},
{
"type": "xychart",
"title": "XY Chart with tooltip options only",
"id": 2,
"options": {
"tooltipOptions": {
"mode": "single"
}
}
},
{
"type": "xychart2",
"title": "XY Chart2 with tooltip options",
"id": 3,
"options": {
"tooltipOptions": {
"mode": "single",
"sort": "none"
}
}
},
{
"type": "graph",
"title": "Graph panel gets migrated to timeseries and tooltip",
"id": 4,
"options": {
"tooltip": {
"mode": "single"
}
}
},
{
"type": "stat",
"title": "Panel with complex value mappings",
"id": 5,
"fieldConfig": {
"defaults": {
"mappings": [
{
"id": 0,
"text": "Critical",
"type": 1,
"value": "100"
},
{
"from": "50",
"to": "99",
"text": "Warning",
"type": 2
},
{
"from": "0",
"to": "49",
"text": "OK",
"type": 2
}
]
}
}
},
{
"type": "row",
"title": "Collapsed Row with nested panels",
"id": 6,
"collapsed": true,
"panels": [
{
"type": "timeseries",
"title": "Nested panel with both migrations",
"id": 7,
"fieldConfig": {
"defaults": {
"mappings": [
{
"id": 0,
"text": "On",
"type": 1,
"value": "1"
},
{
"id": 1,
"text": "Off",
"type": 1,
"value": "0"
}
]
}
},
"options": {
"tooltipOptions": {
"mode": "multi"
}
}
}
]
},
{
"type": "timeseries",
"title": "Panel with no relevant configurations",
"id": 8,
"fieldConfig": {
"defaults": {
"unit": "bytes"
}
},
"options": {
"legend": {
"displayMode": "list"
}
}
},
{
"type": "stat",
"title": "Panel with empty mappings array - should return null",
"id": 9,
"fieldConfig": {
"defaults": {
"mappings": []
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "empty-field"
},
"properties": [
{
"id": "mappings",
"value": []
}
]
}
]
}
}
]
}

@ -0,0 +1,373 @@
{
"panels": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"0": {
"color": "green",
"text": "Down"
},
"1": {
"color": "green",
"text": "Up"
}
},
"type": "value"
},
{
"options": {
"from": 10,
"result": {
"color": "green",
"text": "Medium"
},
"to": 20
},
"type": "range"
},
{
"options": {
"match": "null",
"result": {
"color": "green",
"text": "Null Value"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "test-field"
},
"properties": [
{
"id": "mappings",
"value": [
{
"options": {
"1": {
"text": "Override Up"
}
},
"type": "value"
}
]
}
]
}
]
},
"id": 1,
"options": {
"tooltip": {
"mode": "multi"
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Panel with legacy value mappings and tooltip options",
"type": "timeseries"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 2,
"options": {
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "XY Chart with tooltip options only",
"type": "xychart"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 3,
"options": {
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "XY Chart2 with tooltip options",
"type": "xychart2"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 4,
"options": {
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Graph panel gets migrated to timeseries and tooltip",
"type": "graph"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"100": {
"text": "Critical"
}
},
"type": "value"
},
{
"options": {
"from": 50,
"result": {
"text": "Warning"
},
"to": 99
},
"type": "range"
},
{
"options": {
"from": 0,
"result": {
"text": "OK"
},
"to": 49
},
"type": "range"
}
]
}
},
"id": 5,
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Panel with complex value mappings",
"type": "stat"
},
{
"collapsed": true,
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 6,
"panels": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"0": {
"text": "Off"
},
"1": {
"text": "On"
}
},
"type": "value"
}
]
}
},
"id": 7,
"options": {
"tooltip": {
"mode": "multi"
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Nested panel with both migrations",
"type": "timeseries"
}
],
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Collapsed Row with nested panels",
"type": "row"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"fieldConfig": {
"defaults": {
"unit": "bytes"
}
},
"id": 8,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Panel with no relevant configurations",
"type": "timeseries"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"fieldConfig": {
"defaults": {
"mappings": []
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "empty-field"
},
"properties": [
{
"id": "mappings",
"value": []
}
]
}
]
},
"id": 9,
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Panel with empty mappings array - should return null",
"type": "stat"
}
],
"refresh": "",
"schemaVersion": 41,
"title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard"
}
Loading…
Cancel
Save