Library Panels: Modify connection api endpoint to be compatible with unified storage (#107088)

This commit is contained in:
Stephanie Hingtgen
2025-06-25 22:21:56 +00:00
committed by GitHub
parent 3687767709
commit 79fe8a9902
24 changed files with 621 additions and 50 deletions
@@ -19,6 +19,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrations"
@@ -70,6 +71,33 @@ func (d *dashboardStore) emitEntityEvent() bool {
return d.features != nil && d.features.IsEnabledGlobally(featuremgmt.FlagPanelTitleSearch)
}
func (d *dashboardStore) GetDashboardsByLibraryPanelUID(ctx context.Context, libraryPanelUID string, orgID int64) ([]*dashboards.DashboardRef, error) {
ctx, span := tracer.Start(ctx, "dashboards.database.GetDashboardsByLibraryPanelUID")
defer span.End()
connectedDashboards := make([]*dashboards.DashboardRef, 0)
recursiveQueriesAreSupported, err := d.store.RecursiveQueriesAreSupported()
if err != nil {
return nil, err
}
err = d.store.WithDbSession(ctx, func(session *db.Session) error {
builder := db.NewSqlBuilder(d.cfg, d.features, d.store.GetDialect(), recursiveQueriesAreSupported)
builder.Write("SELECT d.*")
builder.Write(" FROM " + model.LibraryElementConnectionTableName + " AS lec")
builder.Write(" INNER JOIN " + model.LibraryElementTableName + " AS le ON lec.element_id = le.id")
builder.Write(" INNER JOIN dashboard AS d ON lec.connection_id = d.id")
builder.Write(` WHERE le.uid=? AND le.org_id=?`, libraryPanelUID, orgID)
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&connectedDashboards); err != nil {
return err
}
return nil
})
return connectedDashboards, err
}
func (d *dashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dash *dashboards.Dashboard, overwrite bool) (bool, error) {
ctx, span := tracer.Start(ctx, "dashboards.database.ValidateDashboardBeforesave")
defer span.End()
@@ -21,6 +21,7 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
libmodel "github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/search/sort"
@@ -1145,6 +1146,94 @@ func TestIntegrationFindDashboardsByFolder(t *testing.T) {
}
}
func TestIntegrationGetDashboardsByLibraryPanelUID(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
sqlStore, cfg := db.InitTestDBWithCfg(t)
dashboardStore, err := ProvideDashboardStore(sqlStore, cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore))
require.NoError(t, err)
t.Run("Should return dashboards connected to a library panel", func(t *testing.T) {
libraryElement := insertTestLibraryElement(t, sqlStore, "test-library-panel", 1, "", "Test Library Panel")
dash1 := insertTestDashboard(t, dashboardStore, "Dashboard 1", 1, 0, "", false, "prod")
dash2 := insertTestDashboard(t, dashboardStore, "Dashboard 2", 1, 0, "", false, "webapp")
dash3 := insertTestDashboard(t, dashboardStore, "Dashboard 3", 1, 0, "", false, "backend")
// connect all but the last one
insertTestLibraryElementConnection(t, sqlStore, libraryElement.ID, dash1.ID)
insertTestLibraryElementConnection(t, sqlStore, libraryElement.ID, dash2.ID)
connectedDashboards, err := dashboardStore.GetDashboardsByLibraryPanelUID(context.Background(), libraryElement.UID, 1)
require.NoError(t, err)
require.Len(t, connectedDashboards, 2)
uids := []string{connectedDashboards[0].UID, connectedDashboards[1].UID}
require.Contains(t, uids, dash1.UID)
require.Contains(t, uids, dash2.UID)
require.NotContains(t, uids, dash3.UID)
})
t.Run("Returns nothing when library panel has no connections", func(t *testing.T) {
libraryElement := insertTestLibraryElement(t, sqlStore, "empty-library-panel", 1, "", "Empty Library Panel")
connectedDashboards, err := dashboardStore.GetDashboardsByLibraryPanelUID(context.Background(), libraryElement.UID, 1)
require.NoError(t, err)
require.Len(t, connectedDashboards, 0)
})
t.Run("Returns nothing when library panel does not exist", func(t *testing.T) {
connectedDashboards, err := dashboardStore.GetDashboardsByLibraryPanelUID(context.Background(), "non-existent-uid", 1)
require.NoError(t, err)
require.Len(t, connectedDashboards, 0)
})
}
func insertTestLibraryElement(t *testing.T, sqlStore db.DB, uid string, orgID int64, folderUID string, name string) *libmodel.LibraryElement {
t.Helper()
element := &libmodel.LibraryElement{
OrgID: orgID,
FolderUID: folderUID,
UID: uid,
Name: name,
Kind: 1,
Type: "text",
Description: "Test library element",
Model: []byte(`{"type": "text", "content": "test"}`),
Version: 1,
Created: time.Now(),
Updated: time.Now(),
CreatedBy: 1,
UpdatedBy: 1,
}
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
_, err := sess.Insert(element)
return err
})
require.NoError(t, err)
return element
}
func insertTestLibraryElementConnection(t *testing.T, sqlStore db.DB, elementID int64, dashboardID int64) {
t.Helper()
connection := &libmodel.LibraryElementConnection{
ElementID: elementID,
Kind: 1,
ConnectionID: dashboardID,
Created: time.Now(),
CreatedBy: 1,
}
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
_, err := sess.Insert(connection)
return err
})
require.NoError(t, err)
}
func insertTestRule(t *testing.T, sqlStore db.DB, foderOrgID int64, folderUID string) {
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
type alertQuery struct {