PanelLibrary: adding library panels to Dashboard Api (#30278)
* Wip: First naive impl * Chore: fix after merge * Chore: changes after PR comments * Chore: removes unused types * Chore: adds feature toggle * Refactor: adds library panels cleanup and connect when storing dashboards * Refactor: adds feature toggle * Update pkg/services/librarypanels/librarypanels.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Refactor: adds disconnect library panels when deleting a dashboard * Chore: changes after PR comments * Tests: adds tests for LoadLibraryPanelsForDashboard * Tests: adds tests for CleanLibraryPanelsForDashboard * Tests: adds tests for ConnectLibraryPanelsForDashboard * Tests: adds tests for DisconnectLibraryPanelsForDashboard and small refactor * Update pkg/services/librarypanels/librarypanels_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/services/librarypanels/librarypanels_test.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Refactor: deletes all connections in one call and connects all in the same transaction * Chore: adds better comments * Chore: changes after PR comments Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
co-authored by
Arve Knudsen
parent
36dc70e168
commit
b7b6632a4d
@@ -1,8 +1,12 @@
|
||||
package librarypanels
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||
@@ -39,6 +43,157 @@ func (lps *LibraryPanelService) IsEnabled() bool {
|
||||
return lps.Cfg.IsPanelLibraryEnabled()
|
||||
}
|
||||
|
||||
// LoadLibraryPanelsForDashboard loops through all panels in dashboard JSON and replaces any library panel JSON
|
||||
// with JSON stored for library panel in db.
|
||||
func (lps *LibraryPanelService) LoadLibraryPanelsForDashboard(dash *models.Dashboard) error {
|
||||
if !lps.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
libraryPanels, err := lps.getLibraryPanelsForDashboardID(dash.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
panels := dash.Data.Get("panels").MustArray()
|
||||
for i, panel := range panels {
|
||||
panelAsJSON := simplejson.NewFromAny(panel)
|
||||
libraryPanel := panelAsJSON.Get("libraryPanel")
|
||||
if libraryPanel.Interface() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// we have a library panel
|
||||
uid := libraryPanel.Get("uid").MustString()
|
||||
if len(uid) == 0 {
|
||||
return errLibraryPanelHeaderUIDMissing
|
||||
}
|
||||
|
||||
libraryPanelInDB, ok := libraryPanels[uid]
|
||||
if !ok {
|
||||
return fmt.Errorf("found connection to library panel %q that isn't in database", uid)
|
||||
}
|
||||
|
||||
// we have a match between what is stored in db and in dashboard json
|
||||
libraryPanelModel, err := libraryPanelInDB.Model.MarshalJSON()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not marshal library panel JSON: %w", err)
|
||||
}
|
||||
|
||||
libraryPanelModelAsJSON, err := simplejson.NewJson(libraryPanelModel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert library panel to simplejson model: %w", err)
|
||||
}
|
||||
|
||||
// set the library panel json as the new panel json in dashboard json
|
||||
dash.Data.Get("panels").SetIndex(i, libraryPanelModelAsJSON.Interface())
|
||||
|
||||
// set dashboard specific props
|
||||
elem := dash.Data.Get("panels").GetIndex(i)
|
||||
elem.Set("gridPos", panelAsJSON.Get("gridPos").MustMap())
|
||||
elem.Set("id", panelAsJSON.Get("id").MustInt64())
|
||||
elem.Set("libraryPanel", map[string]interface{}{
|
||||
"uid": libraryPanelInDB.UID,
|
||||
"name": libraryPanelInDB.Name,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanLibraryPanelsForDashboard loops through all panels in dashboard JSON and cleans up any library panel JSON so that
|
||||
// only the necessary JSON properties remain when storing the dashboard JSON.
|
||||
func (lps *LibraryPanelService) CleanLibraryPanelsForDashboard(dash *models.Dashboard) error {
|
||||
if !lps.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
panels := dash.Data.Get("panels").MustArray()
|
||||
for i, panel := range panels {
|
||||
panelAsJSON := simplejson.NewFromAny(panel)
|
||||
libraryPanel := panelAsJSON.Get("libraryPanel")
|
||||
if libraryPanel.Interface() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// we have a library panel
|
||||
uid := libraryPanel.Get("uid").MustString()
|
||||
if len(uid) == 0 {
|
||||
return errLibraryPanelHeaderUIDMissing
|
||||
}
|
||||
name := libraryPanel.Get("name").MustString()
|
||||
if len(name) == 0 {
|
||||
return errLibraryPanelHeaderNameMissing
|
||||
}
|
||||
|
||||
// keep only the necessary JSON properties, the rest of the properties should be safely stored in library_panels table
|
||||
gridPos := panelAsJSON.Get("gridPos").MustMap()
|
||||
id := panelAsJSON.Get("id").MustInt64(int64(i))
|
||||
dash.Data.Get("panels").SetIndex(i, map[string]interface{}{
|
||||
"id": id,
|
||||
"gridPos": gridPos,
|
||||
"libraryPanel": map[string]interface{}{
|
||||
"uid": uid,
|
||||
"name": name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectLibraryPanelsForDashboard loops through all panels in dashboard JSON and connects any library panels to the dashboard.
|
||||
func (lps *LibraryPanelService) ConnectLibraryPanelsForDashboard(c *models.ReqContext, dash *models.Dashboard) error {
|
||||
if !lps.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
panels := dash.Data.Get("panels").MustArray()
|
||||
var libraryPanels []string
|
||||
for _, panel := range panels {
|
||||
panelAsJSON := simplejson.NewFromAny(panel)
|
||||
libraryPanel := panelAsJSON.Get("libraryPanel")
|
||||
if libraryPanel.Interface() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// we have a library panel
|
||||
uid := libraryPanel.Get("uid").MustString()
|
||||
if len(uid) == 0 {
|
||||
return errLibraryPanelHeaderUIDMissing
|
||||
}
|
||||
libraryPanels = append(libraryPanels, uid)
|
||||
}
|
||||
|
||||
return lps.connectLibraryPanelsForDashboard(c, libraryPanels, dash.Id)
|
||||
}
|
||||
|
||||
// DisconnectLibraryPanelsForDashboard loops through all panels in dashboard JSON and disconnects any library panels from the dashboard.
|
||||
func (lps *LibraryPanelService) DisconnectLibraryPanelsForDashboard(dash *models.Dashboard) error {
|
||||
if !lps.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
panels := dash.Data.Get("panels").MustArray()
|
||||
panelCount := int64(0)
|
||||
for _, panel := range panels {
|
||||
panelAsJSON := simplejson.NewFromAny(panel)
|
||||
libraryPanel := panelAsJSON.Get("libraryPanel")
|
||||
if libraryPanel.Interface() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// we have a library panel
|
||||
uid := libraryPanel.Get("uid").MustString()
|
||||
if len(uid) == 0 {
|
||||
return errLibraryPanelHeaderUIDMissing
|
||||
}
|
||||
panelCount++
|
||||
}
|
||||
|
||||
return lps.disconnectLibraryPanelsForDashboard(dash.Id, panelCount)
|
||||
}
|
||||
|
||||
// AddMigration defines database migrations.
|
||||
// If Panel Library is not enabled does nothing.
|
||||
func (lps *LibraryPanelService) AddMigration(mg *migrator.Migrator) {
|
||||
|
||||
Reference in New Issue
Block a user