Library Panels: Remove dashboards table dependency when getting all library panels (#99160)

* Remove dashboards table dependency when getting all library panels
* Filter library elements a user can see using the folder service
* Stop using folder name as UID in get all elements tests
* Set actual folder name not UID when getting all elements
* Stop selecting folder name in the get all elements sql query
* Introduce a library elements param selector without where clause
* Include empty string as general folder UID when getting all library elements

---------

Co-authored-by: suntala <arati.rana@grafana.com>
This commit is contained in:
Leonor Oliveira
2025-01-27 10:51:41 +01:00
committed by GitHub
co-authored by suntala
parent 25bb210ca3
commit 0003efa285
7 changed files with 73 additions and 37 deletions
+57 -22
View File
@@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/kinds/librarypanel"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
@@ -277,6 +276,9 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
// getLibraryElements gets a Library Element where param == value
func (l *LibraryElementService) getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signedInUser identity.Requester, params []Pair, features featuremgmt.FeatureToggles, cmd model.GetLibraryElementCommand) ([]model.LibraryElementDTO, error) {
if len(params) < 1 {
return nil, fmt.Errorf("expected at least one parameter pair")
}
libraryElements := make([]model.LibraryElementWithMeta, 0)
recursiveQueriesAreSupported, err := store.RecursiveQueriesAreSupported()
@@ -287,18 +289,16 @@ func (l *LibraryElementService) getLibraryElements(c context.Context, store db.D
err = store.WithDbSession(c, func(session *db.Session) error {
builder := db.NewSqlBuilder(cfg, features, store.GetDialect(), recursiveQueriesAreSupported)
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", ? as folder_name ", cmd.FolderName)
builder.Write(getFromLibraryElementDTOWithMeta(store.GetDialect()))
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
builder.Write(" WHERE ")
// nolint:staticcheck
writeParamSelectorSQL(&builder, append(params, Pair{"folder_id", cmd.FolderID})...)
builder.Write(" UNION ")
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", dashboard.title as folder_name ")
builder.Write(getFromLibraryElementDTOWithMeta(store.GetDialect()))
builder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id <> 0")
builder.Write(" OR le.folder_id <> 0 AND ")
writeParamSelectorSQL(&builder, params...)
builder.Write(` OR dashboard.id=0`)
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryElements); err != nil {
return err
}
@@ -385,6 +385,7 @@ func (l *LibraryElementService) getLibraryElementsByName(c context.Context, sign
return l.getLibraryElements(c, l.SQLStore, l.Cfg, signedInUser, []Pair{{"org_id", signedInUser.GetOrgID()}, {"name", name}}, l.features,
model.GetLibraryElementCommand{
FolderName: dashboards.RootFolderName,
Name: name,
})
}
@@ -416,7 +417,6 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
builder := db.NewSqlBuilder(l.Cfg, l.features, l.SQLStore.GetDialect(), recursiveQueriesAreSupported)
if folderFilter.includeGeneralFolder {
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", 'General' as folder_name ")
builder.Write(", '' as folder_uid ")
builder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
builder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.GetOrgID())
@@ -427,11 +427,9 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
builder.Write(" UNION ")
}
builder.Write(selectLibraryElementDTOWithMeta)
builder.Write(", dashboard.title as folder_name ")
builder.Write(", dashboard.uid as folder_uid ")
builder.Write(", le.folder_uid as folder_uid ")
builder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
builder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id<>0")
builder.Write(` WHERE le.org_id=?`, signedInUser.GetOrgID())
builder.Write(` WHERE le.org_id=? AND le.folder_id<>0`, signedInUser.GetOrgID())
writeKindSQL(query, &builder)
writeSearchStringSQL(query, l.SQLStore, &builder)
writeExcludeSQL(query, &builder)
@@ -439,9 +437,6 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
if err := folderFilter.writeFolderFilterSQL(false, &builder); err != nil {
return err
}
if !signedInUser.HasRole(org.RoleAdmin) {
builder.WriteDashboardPermissionFilter(signedInUser, dashboardaccess.PERMISSION_VIEW, "")
}
if query.SortDirection == search.SortAlphaDesc.Name {
builder.Write(" ORDER BY 1 DESC")
} else {
@@ -454,7 +449,28 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
retDTOs := make([]model.LibraryElementDTO, 0)
// getting all folders a user can see
fs, err := l.folderService.GetFolders(c, folder.GetFoldersQuery{OrgID: signedInUser.GetOrgID(), SignedInUser: signedInUser})
if err != nil {
return err
}
// Every signed in user can see the general folder. The general folder might have "general" or the empty string as its UID.
var folderUIDS = []string{"general", ""}
folderMap := map[string]string{}
for _, f := range fs {
folderUIDS = append(folderUIDS, f.UID)
folderMap[f.UID] = f.Title
}
// if the user is not an admin, we need to filter out elements that are not in folders the user can see
for _, element := range elements {
if !signedInUser.HasRole(org.RoleAdmin) {
if !contains(folderUIDS, element.FolderUID) {
continue
}
}
if folderMap[element.FolderUID] == "" {
folderMap[element.FolderUID] = dashboards.RootFolderName
}
retDTOs = append(retDTOs, model.LibraryElementDTO{
ID: element.ID,
OrgID: element.OrgID,
@@ -468,7 +484,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
Model: element.Model,
Version: element.Version,
Meta: model.LibraryElementDTOMeta{
FolderName: element.FolderName,
FolderName: folderMap[element.FolderUID],
FolderUID: element.FolderUID,
ConnectedDashboards: element.ConnectedDashboards,
Created: element.Created,
@@ -501,8 +517,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
}
countBuilder.Write(selectLibraryElementDTOWithMeta)
countBuilder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
countBuilder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id and le.folder_id<>0")
countBuilder.Write(` WHERE le.org_id=?`, signedInUser.GetOrgID())
countBuilder.Write(` WHERE le.org_id=? AND le.folder_id<>0`, signedInUser.GetOrgID())
writeKindSQL(query, &countBuilder)
writeSearchStringSQL(query, l.SQLStore, &countBuilder)
writeExcludeSQL(query, &countBuilder)
@@ -685,14 +700,25 @@ func (l *LibraryElementService) getConnections(c context.Context, signedInUser i
builder.Write(" LEFT JOIN " + l.SQLStore.GetDialect().Quote("user") + " AS u1 ON lec.created_by = u1.id")
builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id")
builder.Write(` WHERE lec.element_id=?`, element.ID)
if signedInUser.GetOrgRole() != org.RoleAdmin {
builder.WriteDashboardPermissionFilter(signedInUser, dashboardaccess.PERMISSION_VIEW, "")
}
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryElementConnections); err != nil {
return err
}
fs, err := l.folderService.GetFolders(c, folder.GetFoldersQuery{OrgID: signedInUser.GetOrgID(), SignedInUser: signedInUser})
if err != nil {
return err
}
// Every signed in user can see the general folder. The general folder might have "general" or the empty string as its UID.
var folderUIDS = []string{"general", ""}
for _, f := range fs {
folderUIDS = append(folderUIDS, f.UID)
}
for _, connection := range libraryElementConnections {
if !signedInUser.HasRole(org.RoleAdmin) {
if !contains(folderUIDS, element.FolderUID) {
continue
}
}
connections = append(connections, model.LibraryElementConnectionDTO{
ID: connection.ID,
Kind: connection.Kind,
@@ -871,3 +897,12 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
return nil
})
}
func contains(slice []string, element string) bool {
for _, item := range slice {
if item == element {
return true
}
}
return false
}