sqlstore: finish removing Find and SearchDashboards (#49347)

* chore: replace artisnal FakeDashboardService with generated mock

Maintaining a handcrafted FakeDashboardService is not sustainable now that we are in the process of moving the dashboard-related functions out of sqlstore.

* sqlstore: finish removing Find and SearchDashboards

Find and SearchDashboards were previously copied into the dashboard service. This commit completes that work, removing Find and SearchDashboards from the sqlstore and updating callers to use the dashboard service.

* dashboards: remove SearchDashboards from Store interface

SearchDashboards is a wrapper around FindDashboard that transforms the results, so it's been moved out of the Store entirely and the functionality moved into the Dashboard Service's search implementation.

The database tests depended heavily on the transformation, so I added testSearchDashboards, a copy of search dashboards, instead of (heavily) refactoring all the tests.
This commit is contained in:
Kristin Laemmert
2022-05-24 09:24:55 -04:00
committed by GitHub
parent 86871807d2
commit debbb8d59d
15 changed files with 212 additions and 304 deletions

View File

@ -62,7 +62,6 @@ type Store interface {
SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error)
SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error) SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
SavePublicDashboardConfig(cmd models.SavePublicDashboardConfigCommand) (*models.PublicDashboardConfig, error) SavePublicDashboardConfig(cmd models.SavePublicDashboardConfigCommand) (*models.PublicDashboardConfig, error)
SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error
UnprovisionDashboard(ctx context.Context, id int64) error UnprovisionDashboard(ctx context.Context, id int64) error
UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error
// ValidateDashboardBeforeSave validates a dashboard before save. // ValidateDashboardBeforeSave validates a dashboard before save.

View File

@ -930,66 +930,6 @@ func (d *DashboardStore) GetDashboards(ctx context.Context, query *models.GetDas
}) })
} }
func (d *DashboardStore) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
res, err := d.FindDashboards(ctx, query)
if err != nil {
return err
}
makeQueryResult(query, res)
return nil
}
func getHitType(item dashboards.DashboardSearchProjection) models.HitType {
var hitType models.HitType
if item.IsFolder {
hitType = models.DashHitFolder
} else {
hitType = models.DashHitDB
}
return hitType
}
func makeQueryResult(query *models.FindPersistedDashboardsQuery, res []dashboards.DashboardSearchProjection) {
query.Result = make([]*models.Hit, 0)
hits := make(map[int64]*models.Hit)
for _, item := range res {
hit, exists := hits[item.ID]
if !exists {
hit = &models.Hit{
ID: item.ID,
UID: item.UID,
Title: item.Title,
URI: "db/" + item.Slug,
URL: models.GetDashboardFolderUrl(item.IsFolder, item.UID, item.Slug),
Type: getHitType(item),
FolderID: item.FolderID,
FolderUID: item.FolderUID,
FolderTitle: item.FolderTitle,
Tags: []string{},
}
if item.FolderID > 0 {
hit.FolderURL = models.GetFolderUrl(item.FolderUID, item.FolderSlug)
}
if query.Sort.MetaName != "" {
hit.SortMeta = item.SortMeta
hit.SortMetaName = query.Sort.MetaName
}
query.Result = append(query.Result, hit)
hits[item.ID] = hit
}
if len(item.Term) > 0 {
hit.Tags = append(hit.Tags, item.Term)
}
}
}
func (d *DashboardStore) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) { func (d *DashboardStore) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]dashboards.DashboardSearchProjection, error) {
filters := []interface{}{ filters := []interface{}{
permissions.DashboardPermissionFilter{ permissions.DashboardPermissionFilter{

View File

@ -42,7 +42,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
require.Equal(t, query.Result[0].ID, folder.Id) require.Equal(t, query.Result[0].ID, folder.Id)
@ -65,7 +65,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER},
OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
@ -84,7 +84,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
require.Equal(t, query.Result[0].ID, folder.Id) require.Equal(t, query.Result[0].ID, folder.Id)
@ -103,7 +103,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
require.Equal(t, query.Result[0].ID, folder.Id) require.Equal(t, query.Result[0].ID, folder.Id)
@ -125,7 +125,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
query := &models.FindPersistedDashboardsQuery{ query := &models.FindPersistedDashboardsQuery{
SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
require.Equal(t, query.Result[0].ID, dashInRoot.Id) require.Equal(t, query.Result[0].ID, dashInRoot.Id)
@ -139,7 +139,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
t.Run("should be able to search for child dashboard but not folder", func(t *testing.T) { t.Run("should be able to search for child dashboard but not folder", func(t *testing.T) {
query := &models.FindPersistedDashboardsQuery{SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}} query := &models.FindPersistedDashboardsQuery{SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1, DashboardIds: []int64{folder.Id, childDash.Id, dashInRoot.Id}}
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
require.Equal(t, query.Result[0].ID, childDash.Id) require.Equal(t, query.Result[0].ID, childDash.Id)
@ -158,7 +158,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id, childDash.Id}, DashboardIds: []int64{folder.Id, dashInRoot.Id, childDash.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 3) require.Equal(t, len(query.Result), 3)
require.Equal(t, query.Result[0].ID, folder.Id) require.Equal(t, query.Result[0].ID, folder.Id)
@ -197,7 +197,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
}, },
OrgId: 1, OrgId: 1,
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 4) require.Equal(t, len(query.Result), 4)
require.Equal(t, query.Result[0].ID, folder1.Id) require.Equal(t, query.Result[0].ID, folder1.Id)
@ -223,7 +223,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder1.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder1.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
require.Equal(t, query.Result[0].ID, dashInRoot.Id) require.Equal(t, query.Result[0].ID, dashInRoot.Id)
@ -239,7 +239,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
err := sqlStore.SearchDashboards(context.Background(), query) err := testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 4) require.Equal(t, len(query.Result), 4)
require.Equal(t, query.Result[0].ID, folder2.Id) require.Equal(t, query.Result[0].ID, folder2.Id)
@ -263,7 +263,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
OrgId: 1, OrgId: 1,
DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id}, DashboardIds: []int64{folder2.Id, childDash1.Id, childDash2.Id, dashInRoot.Id},
} }
err = sqlStore.SearchDashboards(context.Background(), query) err = testSearchDashboards(dashboardStore, query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 4) require.Equal(t, len(query.Result), 4)
require.Equal(t, query.Result[0].ID, folder2.Id) require.Equal(t, query.Result[0].ID, folder2.Id)
@ -302,7 +302,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
Type: "dash-folder", Type: "dash-folder",
} }
err := sqlStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
@ -355,7 +355,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
} }
t.Run("Should have write access to all dashboard folders with default ACL", func(t *testing.T) { t.Run("Should have write access to all dashboard folders with default ACL", func(t *testing.T) {
err := sqlStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
@ -387,7 +387,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
err = sqlStore.SearchDashboards(context.Background(), &query) err = testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
@ -421,7 +421,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
} }
t.Run("Should have no write access to any dashboard folders with default ACL", func(t *testing.T) { t.Run("Should have no write access to any dashboard folders with default ACL", func(t *testing.T) {
err := sqlStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 0) require.Equal(t, len(query.Result), 0)
@ -453,7 +453,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) {
}) })
require.NoError(t, err) require.NoError(t, err)
err = sqlStore.SearchDashboards(context.Background(), &query) err = testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)

View File

@ -242,10 +242,9 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
SignedInUser: &models.SignedInUser{}, SignedInUser: &models.SignedInUser{},
} }
err = dashboardStore.SearchDashboards(context.Background(), &query) res, err := dashboardStore.FindDashboards(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(res), 0)
require.Equal(t, len(query.Result), 0)
sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
var existingRuleID int64 var existingRuleID int64
@ -301,7 +300,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
}) })
t.Run("Should be able to search for dashboard folder", func(t *testing.T) { t.Run("Should be able to find dashboard folder", func(t *testing.T) {
setup() setup()
query := models.FindPersistedDashboardsQuery{ query := models.FindPersistedDashboardsQuery{
Title: "1 test dash folder", Title: "1 test dash folder",
@ -315,7 +314,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
@ -325,7 +324,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.Equal(t, hit.FolderTitle, "") require.Equal(t, hit.FolderTitle, "")
}) })
t.Run("Should be able to limit search", func(t *testing.T) { t.Run("Should be able to limit find results", func(t *testing.T) {
setup() setup()
query := models.FindPersistedDashboardsQuery{ query := models.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
@ -339,14 +338,14 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
require.EqualValues(t, query.Result[0].Title, "1 test dash folder") require.EqualValues(t, query.Result[0].Title, "1 test dash folder")
}) })
t.Run("Should be able to search beyond limit using paging", func(t *testing.T) { t.Run("Should be able to find results beyond limit using paging", func(t *testing.T) {
setup() setup()
query := models.FindPersistedDashboardsQuery{ query := models.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
@ -364,7 +363,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(query.Result), 1)
@ -386,14 +385,14 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 3) require.Equal(t, len(query.Result), 3)
require.Equal(t, query.Result[0].Title, "test dash 23") require.Equal(t, query.Result[0].Title, "test dash 23")
}) })
t.Run("Should be able to search for a dashboard folder's children", func(t *testing.T) { t.Run("Should be able to find a dashboard folder's children", func(t *testing.T) {
setup() setup()
query := models.FindPersistedDashboardsQuery{ query := models.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
@ -407,7 +406,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
@ -420,7 +419,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.Equal(t, hit.FolderURL, fmt.Sprintf("/dashboards/f/%s/%s", savedFolder.Uid, savedFolder.Slug)) require.Equal(t, hit.FolderURL, fmt.Sprintf("/dashboards/f/%s/%s", savedFolder.Uid, savedFolder.Slug))
}) })
t.Run("Should be able to search for dashboard by dashboard ids", func(t *testing.T) { t.Run("Should be able to find dashboards by ids", func(t *testing.T) {
setup() setup()
query := models.FindPersistedDashboardsQuery{ query := models.FindPersistedDashboardsQuery{
DashboardIds: []int64{savedDash.Id, savedDash2.Id}, DashboardIds: []int64{savedDash.Id, savedDash2.Id},
@ -433,7 +432,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
} }
err := dashboardStore.SearchDashboards(context.Background(), &query) err := testSearchDashboards(dashboardStore, &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 2) require.Equal(t, len(query.Result), 2)
@ -445,7 +444,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.Equal(t, len(hit2.Tags), 1) require.Equal(t, len(hit2.Tags), 1)
}) })
t.Run("Should be able to search for starred dashboards", func(t *testing.T) { t.Run("Should be able to find starred dashboards", func(t *testing.T) {
setup() setup()
starredDash := insertTestDashboard(t, dashboardStore, "starred dash", 1, 0, false) starredDash := insertTestDashboard(t, dashboardStore, "starred dash", 1, 0, false)
err := starService.Add(context.Background(), &star.StarDashboardCommand{ err := starService.Add(context.Background(), &star.StarDashboardCommand{
@ -471,11 +470,11 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
}, },
IsStarred: true, IsStarred: true,
} }
err = dashboardStore.SearchDashboards(context.Background(), &query) res, err := dashboardStore.FindDashboards(context.Background(), &query)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, len(query.Result), 1) require.Equal(t, len(res), 1)
require.Equal(t, query.Result[0].Title, "starred dash") require.Equal(t, res[0].Title, "starred dash")
}) })
} }
@ -516,7 +515,7 @@ func TestIntegrationDashboard_SortingOptions(t *testing.T) {
}, },
}, },
} }
results, err := sqlStore.FindDashboards(context.Background(), qNoSort) results, err := dashboardStore.FindDashboards(context.Background(), qNoSort)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, results, 2) require.Len(t, results, 2)
assert.Equal(t, dashA.Id, results[0].ID) assert.Equal(t, dashA.Id, results[0].ID)
@ -537,7 +536,7 @@ func TestIntegrationDashboard_SortingOptions(t *testing.T) {
}, },
}, },
} }
results, err = sqlStore.FindDashboards(context.Background(), qSort) results, err = dashboardStore.FindDashboards(context.Background(), qSort)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, results, 2) require.Len(t, results, 2)
assert.Equal(t, dashB.Id, results[0].ID) assert.Equal(t, dashB.Id, results[0].ID)
@ -560,7 +559,7 @@ func TestIntegrationDashboard_Filter(t *testing.T) {
}, },
}, },
} }
results, err := sqlStore.FindDashboards(context.Background(), qNoFilter) results, err := dashboardStore.FindDashboards(context.Background(), qNoFilter)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, results, 2) require.Len(t, results, 2)
@ -580,7 +579,7 @@ func TestIntegrationDashboard_Filter(t *testing.T) {
}, },
}, },
} }
results, err = sqlStore.FindDashboards(context.Background(), qFilter) results, err = dashboardStore.FindDashboards(context.Background(), qFilter)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, results, 1) require.Len(t, results, 1)
assert.Equal(t, dashB.Id, results[0].ID) assert.Equal(t, dashB.Id, results[0].ID)
@ -729,3 +728,57 @@ func updateDashboardAcl(t *testing.T, dashboardStore *DashboardStore, dashboardI
return dashboardStore.UpdateDashboardACL(context.Background(), dashboardID, itemPtrs) return dashboardStore.UpdateDashboardACL(context.Background(), dashboardID, itemPtrs)
} }
// testSearchDashboards is a (near) copy of the dashboard service
// SearchDashboards, which is a wrapper around FindDashboards.
func testSearchDashboards(d *DashboardStore, query *models.FindPersistedDashboardsQuery) error {
res, err := d.FindDashboards(context.Background(), query)
if err != nil {
return err
}
makeQueryResult(query, res)
return nil
}
func makeQueryResult(query *models.FindPersistedDashboardsQuery, res []dashboards.DashboardSearchProjection) {
query.Result = make([]*models.Hit, 0)
hits := make(map[int64]*models.Hit)
for _, item := range res {
hit, exists := hits[item.ID]
if !exists {
hitType := models.DashHitDB
if item.IsFolder {
hitType = models.DashHitFolder
}
hit = &models.Hit{
ID: item.ID,
UID: item.UID,
Title: item.Title,
URI: "db/" + item.Slug,
URL: models.GetDashboardFolderUrl(item.IsFolder, item.UID, item.Slug),
Type: hitType,
FolderID: item.FolderID,
FolderUID: item.FolderUID,
FolderTitle: item.FolderTitle,
Tags: []string{},
}
if item.FolderID > 0 {
hit.FolderURL = models.GetFolderUrl(item.FolderUID, item.FolderSlug)
}
if query.Sort.MetaName != "" {
hit.SortMeta = item.SortMeta
hit.SortMetaName = query.Sort.MetaName
}
query.Result = append(query.Result, hit)
hits[item.ID] = hit
}
if len(item.Term) > 0 {
hit.Tags = append(hit.Tags, item.Term)
}
}
}

View File

@ -528,5 +528,61 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *model
} }
func (dr *DashboardServiceImpl) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error { func (dr *DashboardServiceImpl) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
return dr.dashboardStore.SearchDashboards(ctx, query) res, err := dr.FindDashboards(ctx, query)
if err != nil {
return err
}
makeQueryResult(query, res)
return nil
}
func getHitType(item dashboards.DashboardSearchProjection) models.HitType {
var hitType models.HitType
if item.IsFolder {
hitType = models.DashHitFolder
} else {
hitType = models.DashHitDB
}
return hitType
}
func makeQueryResult(query *models.FindPersistedDashboardsQuery, res []dashboards.DashboardSearchProjection) {
query.Result = make([]*models.Hit, 0)
hits := make(map[int64]*models.Hit)
for _, item := range res {
hit, exists := hits[item.ID]
if !exists {
hit = &models.Hit{
ID: item.ID,
UID: item.UID,
Title: item.Title,
URI: "db/" + item.Slug,
URL: models.GetDashboardFolderUrl(item.IsFolder, item.UID, item.Slug),
Type: getHitType(item),
FolderID: item.FolderID,
FolderUID: item.FolderUID,
FolderTitle: item.FolderTitle,
Tags: []string{},
}
if item.FolderID > 0 {
hit.FolderURL = models.GetFolderUrl(item.FolderUID, item.FolderSlug)
}
if query.Sort.MetaName != "" {
hit.SortMeta = item.SortMeta
hit.SortMetaName = query.Sort.MetaName
}
query.Result = append(query.Result, hit)
hits[item.ID] = hit
}
if len(item.Term) > 0 {
hit.Tags = append(hit.Tags, item.Term)
}
}
} }

View File

@ -96,12 +96,13 @@ func (ng *AlertNG) init() error {
var err error var err error
store := &store.DBstore{ store := &store.DBstore{
BaseInterval: ng.Cfg.UnifiedAlerting.BaseInterval, BaseInterval: ng.Cfg.UnifiedAlerting.BaseInterval,
DefaultInterval: ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval, DefaultInterval: ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval,
SQLStore: ng.SQLStore, SQLStore: ng.SQLStore,
Logger: ng.Log, Logger: ng.Log,
FolderService: ng.folderService, FolderService: ng.folderService,
AccessControl: ng.accesscontrol, AccessControl: ng.accesscontrol,
DashboardService: ng.dashboardService,
} }
decryptFn := ng.SecretsService.GetDecryptedValue decryptFn := ng.SecretsService.GetDecryptedValue

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/secrets/database" "github.com/grafana/grafana/pkg/services/secrets/database"
"github.com/go-openapi/strfmt" "github.com/go-openapi/strfmt"
@ -35,10 +36,11 @@ func setupAMTest(t *testing.T) *Alertmanager {
m := metrics.NewAlertmanagerMetrics(prometheus.NewRegistry()) m := metrics.NewAlertmanagerMetrics(prometheus.NewRegistry())
sqlStore := sqlstore.InitTestDB(t) sqlStore := sqlstore.InitTestDB(t)
s := &store.DBstore{ s := &store.DBstore{
BaseInterval: 10 * time.Second, BaseInterval: 10 * time.Second,
DefaultInterval: 60 * time.Second, DefaultInterval: 60 * time.Second,
SQLStore: sqlStore, SQLStore: sqlStore,
Logger: log.New("alertmanager-test"), Logger: log.New("alertmanager-test"),
DashboardService: dashboards.NewFakeDashboardService(t),
} }
kvStore := NewFakeKVStore(t) kvStore := NewFakeKVStore(t)

View File

@ -299,7 +299,7 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use
for { for {
query := searchQuery query := searchQuery
query.Page = page query.Page = page
proj, err := st.SQLStore.FindDashboards(ctx, &query) proj, err := st.DashboardService.FindDashboards(ctx, &query)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -31,9 +31,10 @@ type DBstore struct {
// the base scheduler tick rate; it's used for validating definition interval // the base scheduler tick rate; it's used for validating definition interval
BaseInterval time.Duration BaseInterval time.Duration
// default alert definiiton interval // default alert definiiton interval
DefaultInterval time.Duration DefaultInterval time.Duration
SQLStore *sqlstore.SQLStore SQLStore *sqlstore.SQLStore
Logger log.Logger Logger log.Logger
FolderService dashboards.FolderService FolderService dashboards.FolderService
AccessControl accesscontrol.AccessControl AccessControl accesscontrol.AccessControl
DashboardService dashboards.DashboardService
} }

View File

@ -70,9 +70,10 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, *
) )
require.NoError(t, err) require.NoError(t, err)
return ng, &store.DBstore{ return ng, &store.DBstore{
SQLStore: ng.SQLStore, SQLStore: ng.SQLStore,
BaseInterval: baseInterval * time.Second, BaseInterval: baseInterval * time.Second,
Logger: log.New("ngalert-test"), Logger: log.New("ngalert-test"),
DashboardService: dashboardService,
} }
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"sort" "sort"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/services/star"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
@ -11,15 +12,16 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
) )
func ProvideService(cfg *setting.Cfg, sqlstore *sqlstore.SQLStore, starService star.Service) *SearchService { func ProvideService(cfg *setting.Cfg, sqlstore *sqlstore.SQLStore, starService star.Service, dashboardService dashboards.DashboardService) *SearchService {
s := &SearchService{ s := &SearchService{
Cfg: cfg, Cfg: cfg,
sortOptions: map[string]models.SortOption{ sortOptions: map[string]models.SortOption{
SortAlphaAsc.Name: SortAlphaAsc, SortAlphaAsc.Name: SortAlphaAsc,
SortAlphaDesc.Name: SortAlphaDesc, SortAlphaDesc.Name: SortAlphaDesc,
}, },
sqlstore: sqlstore, sqlstore: sqlstore,
starService: starService, starService: starService,
dashboardService: dashboardService,
} }
return s return s
} }
@ -47,10 +49,11 @@ type Service interface {
} }
type SearchService struct { type SearchService struct {
Cfg *setting.Cfg Cfg *setting.Cfg
sortOptions map[string]models.SortOption sortOptions map[string]models.SortOption
sqlstore sqlstore.Store sqlstore sqlstore.Store
starService star.Service starService star.Service
dashboardService dashboards.DashboardService
} }
func (s *SearchService) SearchHandler(ctx context.Context, query *Query) error { func (s *SearchService) SearchHandler(ctx context.Context, query *Query) error {
@ -71,7 +74,7 @@ func (s *SearchService) SearchHandler(ctx context.Context, query *Query) error {
dashboardQuery.Sort = sortOpt dashboardQuery.Sort = sortOpt
} }
if err := s.sqlstore.SearchDashboards(ctx, &dashboardQuery); err != nil { if err := s.dashboardService.SearchDashboards(ctx, &dashboardQuery); err != nil {
return err return err
} }

View File

@ -4,29 +4,37 @@ import (
"context" "context"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/services/star"
"github.com/grafana/grafana/pkg/services/star/startest" "github.com/grafana/grafana/pkg/services/star/startest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestSearch_SortedResults(t *testing.T) { func TestSearch_SortedResults(t *testing.T) {
ss := startest.NewStarServiceFake() ss := startest.NewStarServiceFake()
ms := mockstore.NewSQLStoreMock() ms := mockstore.NewSQLStoreMock()
ms.ExpectedPersistedDashboards = models.HitList{ ds := dashboards.NewFakeDashboardService(t)
&models.Hit{ID: 16, Title: "CCAA", Type: "dash-db", Tags: []string{"BB", "AA"}}, ds.On("SearchDashboards", mock.Anything, mock.AnythingOfType("*models.FindPersistedDashboardsQuery")).Run(func(args mock.Arguments) {
&models.Hit{ID: 10, Title: "AABB", Type: "dash-db", Tags: []string{"CC", "AA"}}, q := args.Get(1).(*models.FindPersistedDashboardsQuery)
&models.Hit{ID: 15, Title: "BBAA", Type: "dash-db", Tags: []string{"EE", "AA", "BB"}}, q.Result = models.HitList{
&models.Hit{ID: 25, Title: "bbAAa", Type: "dash-db", Tags: []string{"EE", "AA", "BB"}}, &models.Hit{ID: 16, Title: "CCAA", Type: "dash-db", Tags: []string{"BB", "AA"}},
&models.Hit{ID: 17, Title: "FOLDER", Type: "dash-folder"}, &models.Hit{ID: 10, Title: "AABB", Type: "dash-db", Tags: []string{"CC", "AA"}},
} &models.Hit{ID: 15, Title: "BBAA", Type: "dash-db", Tags: []string{"EE", "AA", "BB"}},
&models.Hit{ID: 25, Title: "bbAAa", Type: "dash-db", Tags: []string{"EE", "AA", "BB"}},
&models.Hit{ID: 17, Title: "FOLDER", Type: "dash-folder"},
}
}).Return(nil)
ms.ExpectedSignedInUser = &models.SignedInUser{IsGrafanaAdmin: true} ms.ExpectedSignedInUser = &models.SignedInUser{IsGrafanaAdmin: true}
ss.ExpectedUserStars = &star.GetUserStarsResult{UserStars: map[int64]bool{10: true, 12: true}} ss.ExpectedUserStars = &star.GetUserStarsResult{UserStars: map[int64]bool{10: true, 12: true}}
svc := &SearchService{ svc := &SearchService{
sqlstore: ms, sqlstore: ms,
starService: ss, starService: ss,
dashboardService: ds,
} }
query := &Query{ query := &Query{

View File

@ -7,9 +7,6 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -27,160 +24,6 @@ func init() {
var generateNewUid func() string = util.GenerateShortUID var generateNewUid func() string = util.GenerateShortUID
type DashboardSearchProjection struct {
ID int64 `xorm:"id"`
UID string `xorm:"uid"`
Title string
Slug string
Term string
IsFolder bool
FolderID int64 `xorm:"folder_id"`
FolderUID string `xorm:"folder_uid"`
FolderSlug string
FolderTitle string
SortMeta int64
}
func (ss *SQLStore) FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
filters := []interface{}{
permissions.DashboardPermissionFilter{
OrgRole: query.SignedInUser.OrgRole,
OrgId: query.SignedInUser.OrgId,
Dialect: dialect,
UserId: query.SignedInUser.UserId,
PermissionLevel: query.Permission,
},
}
if !accesscontrol.IsDisabled(ss.Cfg) {
// if access control is enabled, overwrite the filters so far
filters = []interface{}{
permissions.NewAccessControlDashboardPermissionFilter(query.SignedInUser, query.Permission, query.Type),
}
}
for _, filter := range query.Sort.Filter {
filters = append(filters, filter)
}
filters = append(filters, query.Filters...)
if query.OrgId != 0 {
filters = append(filters, searchstore.OrgFilter{OrgId: query.OrgId})
} else if query.SignedInUser.OrgId != 0 {
filters = append(filters, searchstore.OrgFilter{OrgId: query.SignedInUser.OrgId})
}
if len(query.Tags) > 0 {
filters = append(filters, searchstore.TagsFilter{Tags: query.Tags})
}
if len(query.DashboardIds) > 0 {
filters = append(filters, searchstore.DashboardFilter{IDs: query.DashboardIds})
}
if query.IsStarred {
filters = append(filters, searchstore.StarredFilter{UserId: query.SignedInUser.UserId})
}
if len(query.Title) > 0 {
filters = append(filters, searchstore.TitleFilter{Dialect: dialect, Title: query.Title})
}
if len(query.Type) > 0 {
filters = append(filters, searchstore.TypeFilter{Dialect: dialect, Type: query.Type})
}
if len(query.FolderIds) > 0 {
filters = append(filters, searchstore.FolderFilter{IDs: query.FolderIds})
}
var res []DashboardSearchProjection
sb := &searchstore.Builder{Dialect: dialect, Filters: filters}
limit := query.Limit
if limit < 1 {
limit = 1000
}
page := query.Page
if page < 1 {
page = 1
}
sql, params := sb.ToSQL(limit, page)
err := ss.WithDbSession(ctx, func(dbSession *DBSession) error {
return dbSession.SQL(sql, params...).Find(&res)
})
if err != nil {
return nil, err
}
return res, nil
}
func (ss *SQLStore) SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error {
res, err := ss.FindDashboards(ctx, query)
if err != nil {
return err
}
makeQueryResult(query, res)
return nil
}
func getHitType(item DashboardSearchProjection) models.HitType {
var hitType models.HitType
if item.IsFolder {
hitType = models.DashHitFolder
} else {
hitType = models.DashHitDB
}
return hitType
}
func makeQueryResult(query *models.FindPersistedDashboardsQuery, res []DashboardSearchProjection) {
query.Result = make([]*models.Hit, 0)
hits := make(map[int64]*models.Hit)
for _, item := range res {
hit, exists := hits[item.ID]
if !exists {
hit = &models.Hit{
ID: item.ID,
UID: item.UID,
Title: item.Title,
URI: "db/" + item.Slug,
URL: models.GetDashboardFolderUrl(item.IsFolder, item.UID, item.Slug),
Type: getHitType(item),
FolderID: item.FolderID,
FolderUID: item.FolderUID,
FolderTitle: item.FolderTitle,
Tags: []string{},
}
if item.FolderID > 0 {
hit.FolderURL = models.GetFolderUrl(item.FolderUID, item.FolderSlug)
}
if query.Sort.MetaName != "" {
hit.SortMeta = item.SortMeta
hit.SortMetaName = query.Sort.MetaName
}
query.Result = append(query.Result, hit)
hits[item.ID] = hit
}
if len(item.Term) > 0 {
hit.Tags = append(hit.Tags, item.Term)
}
}
}
func (ss *SQLStore) GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error { func (ss *SQLStore) GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error {
return ss.WithDbSession(ctx, func(dbSession *DBSession) error { return ss.WithDbSession(ctx, func(dbSession *DBSession) error {
sql := `SELECT sql := `SELECT

View File

@ -5,14 +5,16 @@ import (
"context" "context"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
const ( const (
@ -43,7 +45,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
Dialect: db.Dialect, Dialect: db.Dialect,
} }
res := []sqlstore.DashboardSearchProjection{} res := []dashboards.DashboardSearchProjection{}
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(limit, page) sql, params := builder.ToSQL(limit, page)
return sess.SQL(sql, params...).Find(&res) return sess.SQL(sql, params...).Find(&res)
@ -52,7 +54,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
assert.Len(t, res, 1) assert.Len(t, res, 1)
res[0].UID = "" res[0].UID = ""
assert.EqualValues(t, []sqlstore.DashboardSearchProjection{ assert.EqualValues(t, []dashboards.DashboardSearchProjection{
{ {
ID: dashIds[0], ID: dashIds[0],
Title: "A", Title: "A",
@ -80,9 +82,9 @@ func TestBuilder_Pagination(t *testing.T) {
Dialect: db.Dialect, Dialect: db.Dialect,
} }
resPg1 := []sqlstore.DashboardSearchProjection{} resPg1 := []dashboards.DashboardSearchProjection{}
resPg2 := []sqlstore.DashboardSearchProjection{} resPg2 := []dashboards.DashboardSearchProjection{}
resPg3 := []sqlstore.DashboardSearchProjection{} resPg3 := []dashboards.DashboardSearchProjection{}
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(15, 1) sql, params := builder.ToSQL(15, 1)
err := sess.SQL(sql, params...).Find(&resPg1) err := sess.SQL(sql, params...).Find(&resPg1)
@ -135,7 +137,7 @@ func TestBuilder_Permissions(t *testing.T) {
Dialect: db.Dialect, Dialect: db.Dialect,
} }
res := []sqlstore.DashboardSearchProjection{} res := []dashboards.DashboardSearchProjection{}
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(limit, page) sql, params := builder.ToSQL(limit, page)
return sess.SQL(sql, params...).Find(&res) return sess.SQL(sql, params...).Find(&res)

View File

@ -97,7 +97,6 @@ type Store interface {
SearchOrgUsers(ctx context.Context, query *models.SearchOrgUsersQuery) error SearchOrgUsers(ctx context.Context, query *models.SearchOrgUsersQuery) error
RemoveOrgUser(ctx context.Context, cmd *models.RemoveOrgUserCommand) error RemoveOrgUser(ctx context.Context, cmd *models.RemoveOrgUserCommand) error
GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error GetDashboardTags(ctx context.Context, query *models.GetDashboardTagsQuery) error
SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error
GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error
GetDataSources(ctx context.Context, query *models.GetDataSourcesQuery) error GetDataSources(ctx context.Context, query *models.GetDataSourcesQuery) error
GetDataSourcesByType(ctx context.Context, query *models.GetDataSourcesByTypeQuery) error GetDataSourcesByType(ctx context.Context, query *models.GetDataSourcesByTypeQuery) error