FindDashboards: filter by dashboard type (#100160)

This commit is contained in:
Stephanie Hingtgen 2025-02-05 17:57:26 -07:00 committed by GitHub
parent f7d476e408
commit 495aa65c6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 176 additions and 10 deletions

View File

@ -46,6 +46,7 @@ import (
"github.com/grafana/grafana/pkg/services/publicdashboards"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -232,7 +233,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardData(ctx context.Context,
for _, org := range orgs {
func(orgID int64) {
g.Go(func() error {
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, dashboards.FindPersistedDashboardsQuery{
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
ProvisionedRepo: name,
OrgId: orgID,
})
@ -273,7 +274,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardID(ctx con
}
for _, org := range orgs {
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, dashboards.FindPersistedDashboardsQuery{
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
OrgId: org.ID,
DashboardIds: []int64{dashboardID},
})
@ -300,7 +301,7 @@ func (dr *DashboardServiceImpl) GetProvisionedDashboardDataByDashboardUID(ctx co
return nil, nil
}
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, dashboards.FindPersistedDashboardsQuery{
res, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
OrgId: orgID,
DashboardUIDs: []string{dashboardUID},
})
@ -559,7 +560,7 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
for _, org := range orgs {
ctx, _ := identity.WithServiceIdentity(ctx, org.ID)
// find all dashboards in the org that have a file repo set that is not in the given readers list
foundDashs, err := dr.searchProvisionedDashboardsThroughK8s(ctx, dashboards.FindPersistedDashboardsQuery{
foundDashs, err := dr.searchProvisionedDashboardsThroughK8s(ctx, &dashboards.FindPersistedDashboardsQuery{
ProvisionedReposNotIn: cmd.ReaderNames,
OrgId: org.ID,
})
@ -1436,7 +1437,12 @@ func (dr *DashboardServiceImpl) DeleteInFolders(ctx context.Context, orgID int64
}
// We need a list of dashboard uids inside the folder to delete related public dashboards
dashes, err := dr.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{SignedInUser: u, FolderUIDs: folderUIDs, OrgId: orgID})
dashes, err := dr.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: u,
FolderUIDs: folderUIDs,
OrgId: orgID,
Type: searchstore.TypeDashboard,
})
if err != nil {
return folder.ErrInternal.Errorf("failed to fetch dashboards: %w", err)
}
@ -1729,7 +1735,11 @@ type dashboardProvisioningWithUID struct {
DashboardUID string
}
func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx context.Context, query dashboards.FindPersistedDashboardsQuery) ([]*dashboardProvisioningWithUID, error) {
func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) ([]*dashboardProvisioningWithUID, error) {
if query == nil {
return nil, errors.New("query cannot be nil")
}
ctx, _ = identity.WithServiceIdentity(ctx, query.OrgId)
if query.ProvisionedRepo != "" {
@ -1744,7 +1754,9 @@ func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx contex
query.ProvisionedReposNotIn = repos
}
searchResults, err := dr.searchDashboardsThroughK8sRaw(ctx, &query)
query.Type = searchstore.TypeDashboard
searchResults, err := dr.searchDashboardsThroughK8sRaw(ctx, query)
if err != nil {
return nil, err
}
@ -1807,6 +1819,11 @@ func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx contex
}
func (dr *DashboardServiceImpl) searchDashboardsThroughK8s(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) ([]*dashboards.Dashboard, error) {
if query == nil {
return nil, errors.New("query cannot be nil")
}
query.Type = searchstore.TypeDashboard
response, err := dr.searchDashboardsThroughK8sRaw(ctx, query)
if err != nil {
return nil, err

View File

@ -1899,6 +1899,134 @@ func TestCountInFolders(t *testing.T) {
})
}
func TestSearchDashboardsThroughK8sRaw(t *testing.T) {
ctx := context.Background()
k8sCliMock := new(client.MockK8sHandler)
service := &DashboardServiceImpl{k8sclient: k8sCliMock}
query := &dashboards.FindPersistedDashboardsQuery{
OrgId: 1,
}
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
Results: &resource.ResourceTable{
Columns: []*resource.ResourceTableColumnDefinition{
{
Name: "title",
Type: resource.ResourceTableColumnDefinition_STRING,
},
{
Name: "folder",
Type: resource.ResourceTableColumnDefinition_STRING,
},
},
Rows: []*resource.ResourceTableRow{
{
Key: &resource.ResourceKey{
Name: "uid",
Resource: "dashboard",
},
Cells: [][]byte{
[]byte("Dashboard 1"),
[]byte("folder1"),
},
},
},
},
TotalHits: 1,
}, nil)
res, err := service.searchDashboardsThroughK8s(ctx, query)
require.NoError(t, err)
assert.Equal(t, []*dashboards.Dashboard{
{
UID: "uid",
OrgID: 1,
FolderUID: "folder1",
Title: "Dashboard 1",
Slug: "dashboard-1", // should be slugified
},
}, res)
assert.Equal(t, "dash-db", query.Type) // query type should be added
}
func TestSearchProvisionedDashboardsThroughK8sRaw(t *testing.T) {
ctx := context.Background()
k8sCliMock := new(client.MockK8sHandler)
service := &DashboardServiceImpl{k8sclient: k8sCliMock}
query := &dashboards.FindPersistedDashboardsQuery{
OrgId: 1,
}
dashboardUnstructuredProvisioned := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"name": "uid",
"annotations": map[string]any{
utils.AnnoKeyRepoName: fileProvisionedRepoPrefix + "test",
utils.AnnoKeyRepoHash: "hash",
utils.AnnoKeyRepoPath: "path/to/file",
utils.AnnoKeyRepoTimestamp: "2025-01-01T00:00:00Z",
},
},
"spec": map[string]any{},
}}
dashboardUnstructuredNotProvisioned := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"name": "uid2",
},
"spec": map[string]any{},
}}
k8sCliMock.On("Search", mock.Anything, mock.Anything, mock.Anything).Return(&resource.ResourceSearchResponse{
Results: &resource.ResourceTable{
Columns: []*resource.ResourceTableColumnDefinition{
{
Name: "title",
Type: resource.ResourceTableColumnDefinition_STRING,
},
{
Name: "folder",
Type: resource.ResourceTableColumnDefinition_STRING,
},
},
Rows: []*resource.ResourceTableRow{
{
Key: &resource.ResourceKey{
Name: "uid",
Resource: "dashboard",
},
Cells: [][]byte{
[]byte("Dashboard 1"),
[]byte("folder1"),
},
},
{
Key: &resource.ResourceKey{
Name: "uid2",
Resource: "dashboard",
},
Cells: [][]byte{
[]byte("Dashboard 2"),
[]byte("folder2"),
},
},
},
},
TotalHits: 1,
}, nil)
k8sCliMock.On("Get", mock.Anything, "uid", mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructuredProvisioned, nil).Once()
k8sCliMock.On("Get", mock.Anything, "uid2", mock.Anything, mock.Anything, mock.Anything).Return(&dashboardUnstructuredNotProvisioned, nil).Once()
res, err := service.searchProvisionedDashboardsThroughK8s(ctx, query)
require.NoError(t, err)
assert.Equal(t, []*dashboardProvisioningWithUID{
{
DashboardUID: "uid",
DashboardProvisioning: dashboards.DashboardProvisioning{
Name: "test",
ExternalID: "path/to/file",
CheckSum: "hash",
Updated: 1735689600,
},
},
}, res) // only should return the one provisioned dashboard
assert.Equal(t, "dash-db", query.Type) // query type should be added as dashboards only
}
func TestLegacySaveCommandToUnstructured(t *testing.T) {
namespace := "test-namespace"
t.Run("successfully converts save command to unstructured", func(t *testing.T) {

View File

@ -37,6 +37,7 @@ import (
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/services/supportbundles"
"github.com/grafana/grafana/pkg/services/user"
@ -1028,7 +1029,12 @@ func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderComm
// if dashboard restore is on we don't delete public dashboards, the hard delete will take care of it later
if !s.features.IsEnabledGlobally(featuremgmt.FlagDashboardRestore) {
// We need a list of dashboard uids inside the folder to delete related public dashboards
dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{SignedInUser: cmd.SignedInUser, FolderUIDs: folderUIDs, OrgId: cmd.OrgID})
dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: cmd.SignedInUser,
FolderUIDs: folderUIDs,
OrgId: cmd.OrgID,
Type: searchstore.TypeDashboard,
})
if err != nil {
return folder.ErrInternal.Errorf("failed to fetch dashboards: %w", err)
}

View File

@ -31,6 +31,7 @@ import (
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/resource"
@ -737,7 +738,12 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol
}
}
} else {
dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{SignedInUser: cmd.SignedInUser, FolderUIDs: folders, OrgId: cmd.OrgID})
dashes, err := s.dashboardStore.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: cmd.SignedInUser,
FolderUIDs: folders,
OrgId: cmd.OrgID,
Type: searchstore.TypeDashboard,
})
if err != nil {
return folder.ErrInternal.Errorf("failed to fetch dashboards: %w", err)
}

View File

@ -21,6 +21,7 @@ import (
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@ -252,6 +253,7 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
// then find the dashboards that were supposed to be connected to this element
_, requester := identity.WithServiceIdentity(c, signedInUser.GetOrgID())
dashs, err := l.dashboardsService.FindDashboards(c, &dashboards.FindPersistedDashboardsQuery{
Type: searchstore.TypeDashboard,
OrgId: signedInUser.GetOrgID(),
DashboardIds: dashboardIDs,
SignedInUser: requester, // a user may be able to delete a library element but not read all dashboards. We still need to run this check, so we don't allow deleting elements if dashboards are connected

View File

@ -25,6 +25,7 @@ import (
"github.com/grafana/grafana/pkg/services/publicdashboards/service/intervalv2"
"github.com/grafana/grafana/pkg/services/publicdashboards/validation"
"github.com/grafana/grafana/pkg/services/query"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@ -375,7 +376,13 @@ func (pd *PublicDashboardServiceImpl) FindAllWithPagination(ctx context.Context,
dashUIDs[i] = pubdash.DashboardUid
}
dashboardsFound, err := pd.dashboardService.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{OrgId: query.OrgID, DashboardUIDs: dashUIDs, SignedInUser: query.User, Limit: int64(len(dashUIDs))})
dashboardsFound, err := pd.dashboardService.FindDashboards(ctx, &dashboards.FindPersistedDashboardsQuery{
OrgId: query.OrgID,
DashboardUIDs: dashUIDs,
SignedInUser: query.User,
Limit: int64(len(dashUIDs)),
Type: searchstore.TypeDashboard,
})
if err != nil {
return nil, ErrInternalServerError.Errorf("FindAllWithPagination: GetDashboards: %w", err)
}