Dashboard Migrations: v26 text2 to text (#108646)

---------

Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
This commit is contained in:
Haris Rozajac
2025-08-06 17:00:48 +00:00
committed by GitHub
co-authored by Ivan Ortega
parent d24e4f984e
commit 1b244cd036
5 changed files with 311 additions and 1 deletions
@@ -5,7 +5,7 @@ import (
)
const (
MIN_VERSION = 26
MIN_VERSION = 25
LATEST_VERSION = 41
)
@@ -38,6 +38,7 @@ type PanelPluginInfoProvider interface {
func GetMigrations(dsInfoProvider DataSourceInfoProvider, panelProvider PanelPluginInfoProvider) map[int]SchemaVersionMigrationFunc {
return map[int]SchemaVersionMigrationFunc{
26: V26,
27: V27,
28: V28(panelProvider),
29: V29,
@@ -0,0 +1,106 @@
package schemaversion
// V26 migration performs two main tasks:
// 1. Converts all text2 panels to text panels by changing the type field
// 2. Removes the angular field from panel options if it exists
//
// The migration includes comprehensive logic from the frontend:
// - Panel type conversion from "text2" to "text"
// - Angular field removal from panel options
// - Support for nested panels in rows
//
// Example before migration:
//
// "panels": [
// {
// "id": 1,
// "type": "text2",
// "title": "Text Panel",
// "options": {
// "content": "Some content",
// "angular": true,
// "mode": "markdown"
// }
// },
// {
// "id": 2,
// "type": "row",
// "title": "Row Panel",
// "panels": [
// {
// "id": 3,
// "type": "text2",
// "title": "Nested Text Panel",
// "options": {
// "content": "Nested content",
// "angular": false,
// "mode": "html"
// }
// }
// ]
// },
// {
// "id": 4,
// "type": "graph",
// "title": "Graph Panel"
// }
// ]
//
// Example after migration:
//
// "panels": [
// {
// "id": 1,
// "type": "text",
// "title": "Text Panel",
// "options": {
// "content": "Some content",
// "mode": "markdown"
// }
// },
// {
// "id": 2,
// "type": "row",
// "title": "Row Panel",
// "panels": [
// {
// "id": 3,
// "type": "text",
// "title": "Nested Text Panel",
// "options": {
// "content": "Nested content",
// "mode": "html"
// }
// }
// ]
// },
// {
// "id": 4,
// "type": "graph",
// "title": "Graph Panel"
// }
// ]
func V26(dashboard map[string]interface{}) error {
dashboard["schemaVersion"] = 26
panels, ok := dashboard["panels"].([]interface{})
if !ok {
return nil
}
for _, panel := range panels {
panelMap, ok := panel.(map[string]interface{})
if !ok {
continue
}
if panelMap["type"] == "text2" {
panelMap["type"] = "text"
if options, ok := panelMap["options"].(map[string]interface{}); ok {
delete(options, "angular")
}
}
}
return nil
}
@@ -0,0 +1,100 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
func TestV26(t *testing.T) {
tests := []migrationTestCase{
{
name: "migrate text2 to text",
input: map[string]interface{}{
"schemaVersion": 25,
"panels": []interface{}{
map[string]interface{}{
"id": 1,
"type": "text2",
"title": "Text2 Panel",
},
},
},
expected: map[string]interface{}{
"schemaVersion": 26,
"panels": []interface{}{
map[string]interface{}{
"id": 1,
"type": "text",
"title": "Text2 Panel",
},
},
},
},
{
name: "should not migrate panel with old text panel id",
input: map[string]interface{}{
"schemaVersion": 25,
"panels": []interface{}{
map[string]interface{}{
"id": 2,
"type": "text",
"title": "Angular Text Panel",
"content": "# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown",
},
},
},
expected: map[string]interface{}{
"schemaVersion": 26,
"panels": []interface{}{
map[string]interface{}{
"id": 2,
"type": "text",
"title": "Angular Text Panel",
"content": "# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown",
},
},
},
},
{
name: "should clean up old angular options for panels with new Text Panel id",
input: map[string]interface{}{
"schemaVersion": 25,
"panels": []interface{}{
map[string]interface{}{
"id": 3,
"type": "text2",
"title": "React Text Panel from Angular Panel",
"options": map[string]interface{}{
"mode": "markdown",
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"angular": map[string]interface{}{
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown",
"options": map[string]interface{}{},
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 26,
"panels": []interface{}{
map[string]interface{}{
"id": 3,
"type": "text",
"title": "React Text Panel from Angular Panel",
"options": map[string]interface{}{
"mode": "markdown",
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
},
},
},
},
},
}
runMigrationTests(t, tests, schemaversion.V26)
}
@@ -0,0 +1,31 @@
{
"schemaVersion": 25,
"panels": [
{
"id": 1,
"type": "text2",
"title": "Text2 Panel"
},
{
"id": 2,
"type": "text",
"title": "Angular Text Panel",
"content": "# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown"
},
{
"id": 3,
"type": "text2",
"title": "React Text Panel from Angular Panel",
"options": {
"mode": "markdown",
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"angular": {
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown",
"options": {}
}
}
}
]
}
@@ -0,0 +1,72 @@
{
"panels": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 1,
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Text2 Panel",
"type": "text"
},
{
"content": "# Angular Text Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 2,
"mode": "markdown",
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "Angular Text Panel",
"type": "text"
},
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"id": 3,
"options": {
"content": "# React Text Panel from Angular Panel\n# $constant\n\nFor markdown syntax help: [commonmark.org/help](https://commonmark.org/help/)\n\n## $text\n\n",
"mode": "markdown"
},
"targets": [
{
"datasource": {
"apiVersion": "v1",
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "React Text Panel from Angular Panel",
"type": "text"
}
],
"refresh": "",
"schemaVersion": 41
}