Dashboard Migrations: V29 query variable refresh property and options (#108088)

* Dashboard Migrations: V31 LabelsToFields-Merge Migration

* Dashboard Migrations: V32 No-op migration

* simplify

* Refactor to reduce nesting

* Dashboard Migrations: V30 value mappings and tooltip options

* Do not automigrate since graph is migrated in v27

* Refactor to reduce nesting

* Add test case for invalid mapping

* migrate to v29

---------

Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
adela/expose_hidden_fields^2
Haris Rozajac 5 days ago committed by GitHub
parent 1200b684c6
commit 2f8ec01c6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      apps/dashboard/pkg/migration/schemaversion/migrations.go
  2. 59
      apps/dashboard/pkg/migration/schemaversion/v29.go
  3. 127
      apps/dashboard/pkg/migration/schemaversion/v29_test.go
  4. 137
      apps/dashboard/pkg/migration/testdata/input/v29.query_variables_refresh_and_options.json
  5. 176
      apps/dashboard/pkg/migration/testdata/output/v29.query_variables_refresh_and_options.json
  6. 2
      public/app/features/dashboard/state/DashboardMigrator.test.ts

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

@ -0,0 +1,59 @@
package schemaversion
// V29 migrates query variables to ensure their refresh property is set to 1 (on dashboard load)
// if it is not 1 or 2, and clears their options array if present.
//
// Example before migration:
//
// "templating": {
// "list": [
// { "type": "query", "refresh": 0, "options": [{ "text": "A", "value": "A" }] },
// { "type": "query", "refresh": 2, "options": [{ "text": "B", "value": "B" }] },
// { "type": "query", "options": [{ "text": "C", "value": "C" }] }
// ]
// }
//
// Example after migration:
//
// "templating": {
// "list": [
// { "type": "query", "refresh": 1, "options": [] },
// { "type": "query", "refresh": 2, "options": [] },
// { "type": "query", "refresh": 1, "options": [] }
// ]
// }
func V29(dashboard map[string]interface{}) error {
dashboard["schemaVersion"] = 29
templating, ok := dashboard["templating"].(map[string]interface{})
if !ok {
return nil
}
list, ok := templating["list"].([]interface{})
if !ok {
return nil
}
for _, v := range list {
variable, ok := v.(map[string]interface{})
if !ok {
continue
}
if variable["type"] != "query" {
continue
}
// Set refresh to 1 if not 1 or 2
refresh, hasRefresh := variable["refresh"]
refreshInt := 0
if r, ok := refresh.(int); ok {
refreshInt = r
}
if !hasRefresh || (refreshInt != 1 && refreshInt != 2) {
variable["refresh"] = 1
}
// Clear options if present
if _, hasOptions := variable["options"]; hasOptions {
variable["options"] = []interface{}{}
}
}
return nil
}

@ -0,0 +1,127 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
func TestV29(t *testing.T) {
tests := []migrationTestCase{
{
name: "query variables get migrated with refresh and options",
input: map[string]interface{}{
"title": "V29 Query Variables Migration Test Dashboard",
"schemaVersion": 28,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "query", "name": "never_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 0},
map[string]interface{}{"type": "query", "name": "never_refresh_without_options", "options": []interface{}{}, "refresh": 0},
map[string]interface{}{"type": "query", "name": "dashboard_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "dashboard_refresh_without_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "timerange_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "timerange_refresh_without_options", "options": []interface{}{}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "no_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}},
map[string]interface{}{"type": "query", "name": "no_refresh_without_options", "options": []interface{}{}},
map[string]interface{}{"type": "query", "name": "unknown_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 2001},
map[string]interface{}{"type": "query", "name": "unknown_refresh_without_options", "options": []interface{}{}, "refresh": 2001},
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
},
},
},
expected: map[string]interface{}{
"title": "V29 Query Variables Migration Test Dashboard",
"schemaVersion": 29,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "query", "name": "never_refresh_with_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "never_refresh_without_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "dashboard_refresh_with_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "dashboard_refresh_without_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "timerange_refresh_with_options", "options": []interface{}{}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "timerange_refresh_without_options", "options": []interface{}{}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "no_refresh_with_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "no_refresh_without_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "unknown_refresh_with_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "unknown_refresh_without_options", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
},
},
},
},
{
name: "non-query variables remain unchanged",
input: map[string]interface{}{
"title": "V29 Non-Query Variables Test Dashboard",
"schemaVersion": 28,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
},
},
},
expected: map[string]interface{}{
"title": "V29 Non-Query Variables Test Dashboard",
"schemaVersion": 29,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
},
},
},
},
{
name: "all query variables should have options removed",
input: map[string]interface{}{
"title": "V29 Query Variables Options Removal Test Dashboard",
"schemaVersion": 28,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "query", "name": "query1", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}},
map[string]interface{}{"type": "query", "name": "query2", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query3", "options": []interface{}{map[string]interface{}{"text": "B", "value": "B"}, map[string]interface{}{"text": "C", "value": "C"}}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "query4", "options": []interface{}{map[string]interface{}{"text": "D", "value": "D"}}, "refresh": 0},
map[string]interface{}{"type": "query", "name": "query5", "options": []interface{}{map[string]interface{}{"text": "E", "value": "E"}}, "refresh": 2001},
map[string]interface{}{"type": "query", "name": "query6", "options": []interface{}{}},
map[string]interface{}{"type": "query", "name": "query7", "options": []interface{}{map[string]interface{}{"text": "F", "value": "F"}}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query8", "options": []interface{}{map[string]interface{}{"text": "G", "value": "G"}}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "query9", "options": []interface{}{map[string]interface{}{"text": "H", "value": "H"}}},
map[string]interface{}{"type": "query", "name": "query10", "options": []interface{}{map[string]interface{}{"text": "I", "value": "I"}}, "refresh": 999},
},
},
},
expected: map[string]interface{}{
"title": "V29 Query Variables Options Removal Test Dashboard",
"schemaVersion": 29,
"templating": map[string]interface{}{
"list": []interface{}{
map[string]interface{}{"type": "query", "name": "query1", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query2", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query3", "options": []interface{}{}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "query4", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query5", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query6", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query7", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query8", "options": []interface{}{}, "refresh": 2},
map[string]interface{}{"type": "query", "name": "query9", "options": []interface{}{}, "refresh": 1},
map[string]interface{}{"type": "query", "name": "query10", "options": []interface{}{}, "refresh": 1},
},
},
},
},
}
runMigrationTests(t, tests, schemaversion.V29)
}

@ -0,0 +1,137 @@
{
"title": "V29 Query Variables Refresh and Options Migration Test Dashboard",
"schemaVersion": 28,
"templating": {
"list": [
{
"name": "never_refresh_with_options",
"type": "query",
"datasource": "prometheus",
"options": [
{"text": "A", "value": "A"},
{"text": "B", "value": "B"}
],
"refresh": 0
},
{
"name": "never_refresh_without_options",
"type": "query",
"datasource": "prometheus",
"options": [],
"refresh": 0
},
{
"name": "dashboard_refresh_with_options",
"type": "query",
"datasource": "prometheus",
"options": [
{"text": "C", "value": "C"}
],
"refresh": 1
},
{
"name": "dashboard_refresh_without_options",
"type": "query",
"datasource": "prometheus",
"options": [],
"refresh": 1
},
{
"name": "timerange_refresh_with_options",
"type": "query",
"datasource": "prometheus",
"options": [
{"text": "D", "value": "D"},
{"text": "E", "value": "E"}
],
"refresh": 2
},
{
"name": "timerange_refresh_without_options",
"type": "query",
"datasource": "prometheus",
"options": [],
"refresh": 2
},
{
"name": "no_refresh_with_options",
"type": "query",
"datasource": "prometheus",
"options": [
{"text": "F", "value": "F"}
]
},
{
"name": "no_refresh_without_options",
"type": "query",
"datasource": "prometheus",
"options": []
},
{
"name": "unknown_refresh_with_options",
"type": "query",
"datasource": "prometheus",
"options": [
{"text": "G", "value": "G"}
],
"refresh": 2001
},
{
"name": "unknown_refresh_without_options",
"type": "query",
"datasource": "prometheus",
"options": [],
"refresh": 2001
},
{
"name": "custom_variable",
"type": "custom",
"options": [
{"text": "custom", "value": "custom"}
]
},
{
"name": "textbox_variable",
"type": "textbox",
"options": [
{"text": "Hello", "value": "World"}
]
},
{
"name": "datasource_variable",
"type": "datasource",
"options": [
{"text": "ds", "value": "ds"}
]
},
{
"name": "interval_variable",
"type": "interval",
"options": [
{"text": "1m", "value": "1m"}
]
}
]
},
"panels": [
{
"id": 1,
"title": "Test Panel",
"type": "timeseries",
"datasource": "prometheus",
"targets": [
{
"refId": "A",
"expr": "up"
}
]
}
],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
}
}

@ -0,0 +1,176 @@
{
"panels": [
{
"datasource": {
"uid": "prometheus"
},
"id": 1,
"targets": [
{
"datasource": {
"uid": "prometheus"
},
"expr": "up",
"refId": "A"
}
],
"title": "Test Panel",
"type": "timeseries"
}
],
"refresh": "",
"schemaVersion": 41,
"templating": {
"list": [
{
"datasource": {
"uid": "prometheus"
},
"name": "never_refresh_with_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "never_refresh_without_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "dashboard_refresh_with_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "dashboard_refresh_without_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "timerange_refresh_with_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "timerange_refresh_without_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "no_refresh_with_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "no_refresh_without_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "unknown_refresh_with_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"datasource": {
"uid": "prometheus"
},
"name": "unknown_refresh_without_options",
"options": [],
"refresh": 1,
"type": "query"
},
{
"name": "custom_variable",
"options": [
{
"text": "custom",
"value": "custom"
}
],
"type": "custom"
},
{
"name": "textbox_variable",
"options": [
{
"text": "Hello",
"value": "World"
}
],
"type": "textbox"
},
{
"name": "datasource_variable",
"options": [
{
"text": "ds",
"value": "ds"
}
],
"type": "datasource"
},
{
"name": "interval_variable",
"options": [
{
"text": "1m",
"value": "1m"
}
],
"type": "interval"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"title": "V29 Query Variables Refresh and Options Migration Test Dashboard"
}

@ -1043,7 +1043,7 @@ describe('DashboardModel', () => {
});
});
it('should have 11 variables after migration', () => {
it('should have 14 variables after migration', () => {
expect(model.templating.list.length).toBe(14);
});

Loading…
Cancel
Save