diff --git a/pkg/services/dashboards/dashboard.go b/pkg/services/dashboards/dashboard.go index b50af60fd64..3797a355237 100644 --- a/pkg/services/dashboards/dashboard.go +++ b/pkg/services/dashboards/dashboard.go @@ -62,7 +62,6 @@ type Store interface { SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error) SavePublicDashboardConfig(cmd models.SavePublicDashboardConfigCommand) (*models.PublicDashboardConfig, error) - SearchDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) error UnprovisionDashboard(ctx context.Context, id int64) error UpdateDashboardACL(ctx context.Context, uid int64, items []*models.DashboardAcl) error // ValidateDashboardBeforeSave validates a dashboard before save. diff --git a/pkg/services/dashboards/database/database.go b/pkg/services/dashboards/database/database.go index 684477e7291..533afb7d2a7 100644 --- a/pkg/services/dashboards/database/database.go +++ b/pkg/services/dashboards/database/database.go @@ -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) { filters := []interface{}{ permissions.DashboardPermissionFilter{ diff --git a/pkg/services/dashboards/database/database_folder_test.go b/pkg/services/dashboards/database/database_folder_test.go index ae38bb2b85e..dc1e07bf34c 100644 --- a/pkg/services/dashboards/database/database_folder_test.go +++ b/pkg/services/dashboards/database/database_folder_test.go @@ -42,7 +42,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 2) 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}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 1) @@ -84,7 +84,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 2) require.Equal(t, query.Result[0].ID, folder.Id) @@ -103,7 +103,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 2) require.Equal(t, query.Result[0].ID, folder.Id) @@ -125,7 +125,7 @@ func TestIntegrationDashboardFolderDataAccess(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}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 1) 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) { 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.Equal(t, len(query.Result), 2) require.Equal(t, query.Result[0].ID, childDash.Id) @@ -158,7 +158,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id, childDash.Id}, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 3) require.Equal(t, query.Result[0].ID, folder.Id) @@ -197,7 +197,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { }, OrgId: 1, } - err := sqlStore.SearchDashboards(context.Background(), query) + err := testSearchDashboards(dashboardStore, query) require.NoError(t, err) require.Equal(t, len(query.Result), 4) require.Equal(t, query.Result[0].ID, folder1.Id) @@ -223,7 +223,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, 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.Equal(t, len(query.Result), 1) require.Equal(t, query.Result[0].ID, dashInRoot.Id) @@ -239,7 +239,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, 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.Equal(t, len(query.Result), 4) require.Equal(t, query.Result[0].ID, folder2.Id) @@ -263,7 +263,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { OrgId: 1, 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.Equal(t, len(query.Result), 4) require.Equal(t, query.Result[0].ID, folder2.Id) @@ -302,7 +302,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { Type: "dash-folder", } - err := sqlStore.SearchDashboards(context.Background(), &query) + err := testSearchDashboards(dashboardStore, &query) require.NoError(t, err) 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) { - err := sqlStore.SearchDashboards(context.Background(), &query) + err := testSearchDashboards(dashboardStore, &query) require.NoError(t, err) require.Equal(t, len(query.Result), 2) @@ -387,7 +387,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { }) require.NoError(t, err) - err = sqlStore.SearchDashboards(context.Background(), &query) + err = testSearchDashboards(dashboardStore, &query) require.NoError(t, err) 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) { - err := sqlStore.SearchDashboards(context.Background(), &query) + err := testSearchDashboards(dashboardStore, &query) require.NoError(t, err) require.Equal(t, len(query.Result), 0) @@ -453,7 +453,7 @@ func TestIntegrationDashboardFolderDataAccess(t *testing.T) { }) require.NoError(t, err) - err = sqlStore.SearchDashboards(context.Background(), &query) + err = testSearchDashboards(dashboardStore, &query) require.NoError(t, err) require.Equal(t, len(query.Result), 1) diff --git a/pkg/services/dashboards/database/database_test.go b/pkg/services/dashboards/database/database_test.go index c568f646b7c..7ad04c3e053 100644 --- a/pkg/services/dashboards/database/database_test.go +++ b/pkg/services/dashboards/database/database_test.go @@ -242,10 +242,9 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { SignedInUser: &models.SignedInUser{}, } - err = dashboardStore.SearchDashboards(context.Background(), &query) + res, err := dashboardStore.FindDashboards(context.Background(), &query) require.NoError(t, err) - - require.Equal(t, len(query.Result), 0) + require.Equal(t, len(res), 0) sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { var existingRuleID int64 @@ -301,7 +300,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { 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() query := models.FindPersistedDashboardsQuery{ 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.Equal(t, len(query.Result), 1) @@ -325,7 +324,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { 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() query := models.FindPersistedDashboardsQuery{ 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.Equal(t, len(query.Result), 1) 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() query := models.FindPersistedDashboardsQuery{ 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.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.Equal(t, len(query.Result), 3) 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() query := models.FindPersistedDashboardsQuery{ 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.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)) }) - 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() query := models.FindPersistedDashboardsQuery{ 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.Equal(t, len(query.Result), 2) @@ -445,7 +444,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { 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() starredDash := insertTestDashboard(t, dashboardStore, "starred dash", 1, 0, false) err := starService.Add(context.Background(), &star.StarDashboardCommand{ @@ -471,11 +470,11 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { }, IsStarred: true, } - err = dashboardStore.SearchDashboards(context.Background(), &query) + res, err := dashboardStore.FindDashboards(context.Background(), &query) require.NoError(t, err) - require.Equal(t, len(query.Result), 1) - require.Equal(t, query.Result[0].Title, "starred dash") + require.Equal(t, len(res), 1) + 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.Len(t, results, 2) 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.Len(t, results, 2) 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.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.Len(t, results, 1) 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) } + +// 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) + } + } +} diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 2be7f6fc686..a035374fde5 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -528,5 +528,61 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *model } 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) + } + } } diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go index 6c152ea138b..6ae853e1326 100644 --- a/pkg/services/ngalert/ngalert.go +++ b/pkg/services/ngalert/ngalert.go @@ -96,12 +96,13 @@ func (ng *AlertNG) init() error { var err error store := &store.DBstore{ - BaseInterval: ng.Cfg.UnifiedAlerting.BaseInterval, - DefaultInterval: ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval, - SQLStore: ng.SQLStore, - Logger: ng.Log, - FolderService: ng.folderService, - AccessControl: ng.accesscontrol, + BaseInterval: ng.Cfg.UnifiedAlerting.BaseInterval, + DefaultInterval: ng.Cfg.UnifiedAlerting.DefaultRuleEvaluationInterval, + SQLStore: ng.SQLStore, + Logger: ng.Log, + FolderService: ng.folderService, + AccessControl: ng.accesscontrol, + DashboardService: ng.dashboardService, } decryptFn := ng.SecretsService.GetDecryptedValue diff --git a/pkg/services/ngalert/notifier/alertmanager_test.go b/pkg/services/ngalert/notifier/alertmanager_test.go index 5e2f10e7420..c308e800dca 100644 --- a/pkg/services/ngalert/notifier/alertmanager_test.go +++ b/pkg/services/ngalert/notifier/alertmanager_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/secrets/database" "github.com/go-openapi/strfmt" @@ -35,10 +36,11 @@ func setupAMTest(t *testing.T) *Alertmanager { m := metrics.NewAlertmanagerMetrics(prometheus.NewRegistry()) sqlStore := sqlstore.InitTestDB(t) s := &store.DBstore{ - BaseInterval: 10 * time.Second, - DefaultInterval: 60 * time.Second, - SQLStore: sqlStore, - Logger: log.New("alertmanager-test"), + BaseInterval: 10 * time.Second, + DefaultInterval: 60 * time.Second, + SQLStore: sqlStore, + Logger: log.New("alertmanager-test"), + DashboardService: dashboards.NewFakeDashboardService(t), } kvStore := NewFakeKVStore(t) diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 0402942bc8e..f3c0319e76a 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -299,7 +299,7 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use for { query := searchQuery query.Page = page - proj, err := st.SQLStore.FindDashboards(ctx, &query) + proj, err := st.DashboardService.FindDashboards(ctx, &query) if err != nil { return nil, err } diff --git a/pkg/services/ngalert/store/database.go b/pkg/services/ngalert/store/database.go index 52ea19e7029..7e0d40ae94b 100644 --- a/pkg/services/ngalert/store/database.go +++ b/pkg/services/ngalert/store/database.go @@ -31,9 +31,10 @@ type DBstore struct { // the base scheduler tick rate; it's used for validating definition interval BaseInterval time.Duration // default alert definiiton interval - DefaultInterval time.Duration - SQLStore *sqlstore.SQLStore - Logger log.Logger - FolderService dashboards.FolderService - AccessControl accesscontrol.AccessControl + DefaultInterval time.Duration + SQLStore *sqlstore.SQLStore + Logger log.Logger + FolderService dashboards.FolderService + AccessControl accesscontrol.AccessControl + DashboardService dashboards.DashboardService } diff --git a/pkg/services/ngalert/tests/util.go b/pkg/services/ngalert/tests/util.go index 20954caf973..373de7d4801 100644 --- a/pkg/services/ngalert/tests/util.go +++ b/pkg/services/ngalert/tests/util.go @@ -70,9 +70,10 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, * ) require.NoError(t, err) return ng, &store.DBstore{ - SQLStore: ng.SQLStore, - BaseInterval: baseInterval * time.Second, - Logger: log.New("ngalert-test"), + SQLStore: ng.SQLStore, + BaseInterval: baseInterval * time.Second, + Logger: log.New("ngalert-test"), + DashboardService: dashboardService, } } diff --git a/pkg/services/search/service.go b/pkg/services/search/service.go index fc5c1a0bf06..6cfddb57eb7 100644 --- a/pkg/services/search/service.go +++ b/pkg/services/search/service.go @@ -4,6 +4,7 @@ import ( "context" "sort" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/setting" @@ -11,15 +12,16 @@ import ( "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{ Cfg: cfg, sortOptions: map[string]models.SortOption{ SortAlphaAsc.Name: SortAlphaAsc, SortAlphaDesc.Name: SortAlphaDesc, }, - sqlstore: sqlstore, - starService: starService, + sqlstore: sqlstore, + starService: starService, + dashboardService: dashboardService, } return s } @@ -47,10 +49,11 @@ type Service interface { } type SearchService struct { - Cfg *setting.Cfg - sortOptions map[string]models.SortOption - sqlstore sqlstore.Store - starService star.Service + Cfg *setting.Cfg + sortOptions map[string]models.SortOption + sqlstore sqlstore.Store + starService star.Service + dashboardService dashboards.DashboardService } 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 } - if err := s.sqlstore.SearchDashboards(ctx, &dashboardQuery); err != nil { + if err := s.dashboardService.SearchDashboards(ctx, &dashboardQuery); err != nil { return err } diff --git a/pkg/services/search/service_test.go b/pkg/services/search/service_test.go index b3ac23e60a0..9d9ba74d77e 100644 --- a/pkg/services/search/service_test.go +++ b/pkg/services/search/service_test.go @@ -4,29 +4,37 @@ import ( "context" "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/services/dashboards" "github.com/grafana/grafana/pkg/services/sqlstore/mockstore" "github.com/grafana/grafana/pkg/services/star" "github.com/grafana/grafana/pkg/services/star/startest" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestSearch_SortedResults(t *testing.T) { ss := startest.NewStarServiceFake() ms := mockstore.NewSQLStoreMock() - ms.ExpectedPersistedDashboards = models.HitList{ - &models.Hit{ID: 16, Title: "CCAA", Type: "dash-db", Tags: []string{"BB", "AA"}}, - &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"}, - } + ds := dashboards.NewFakeDashboardService(t) + ds.On("SearchDashboards", mock.Anything, mock.AnythingOfType("*models.FindPersistedDashboardsQuery")).Run(func(args mock.Arguments) { + q := args.Get(1).(*models.FindPersistedDashboardsQuery) + q.Result = models.HitList{ + &models.Hit{ID: 16, Title: "CCAA", Type: "dash-db", Tags: []string{"BB", "AA"}}, + &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} ss.ExpectedUserStars = &star.GetUserStarsResult{UserStars: map[int64]bool{10: true, 12: true}} svc := &SearchService{ - sqlstore: ms, - starService: ss, + sqlstore: ms, + starService: ss, + dashboardService: ds, } query := &Query{ diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 7f77158e748..5d500baa12a 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -7,9 +7,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "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" ) @@ -27,160 +24,6 @@ func init() { 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 { return ss.WithDbSession(ctx, func(dbSession *DBSession) error { sql := `SELECT diff --git a/pkg/services/sqlstore/searchstore/search_test.go b/pkg/services/sqlstore/searchstore/search_test.go index 1d17b593d50..4b4dd130cb5 100644 --- a/pkg/services/sqlstore/searchstore/search_test.go +++ b/pkg/services/sqlstore/searchstore/search_test.go @@ -5,14 +5,16 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/grafana/grafana/pkg/components/simplejson" "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/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) const ( @@ -43,7 +45,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) { Dialect: db.Dialect, } - res := []sqlstore.DashboardSearchProjection{} + res := []dashboards.DashboardSearchProjection{} err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { sql, params := builder.ToSQL(limit, page) return sess.SQL(sql, params...).Find(&res) @@ -52,7 +54,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) { assert.Len(t, res, 1) res[0].UID = "" - assert.EqualValues(t, []sqlstore.DashboardSearchProjection{ + assert.EqualValues(t, []dashboards.DashboardSearchProjection{ { ID: dashIds[0], Title: "A", @@ -80,9 +82,9 @@ func TestBuilder_Pagination(t *testing.T) { Dialect: db.Dialect, } - resPg1 := []sqlstore.DashboardSearchProjection{} - resPg2 := []sqlstore.DashboardSearchProjection{} - resPg3 := []sqlstore.DashboardSearchProjection{} + resPg1 := []dashboards.DashboardSearchProjection{} + resPg2 := []dashboards.DashboardSearchProjection{} + resPg3 := []dashboards.DashboardSearchProjection{} err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { sql, params := builder.ToSQL(15, 1) err := sess.SQL(sql, params...).Find(&resPg1) @@ -135,7 +137,7 @@ func TestBuilder_Permissions(t *testing.T) { Dialect: db.Dialect, } - res := []sqlstore.DashboardSearchProjection{} + res := []dashboards.DashboardSearchProjection{} err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { sql, params := builder.ToSQL(limit, page) return sess.SQL(sql, params...).Find(&res) diff --git a/pkg/services/sqlstore/store.go b/pkg/services/sqlstore/store.go index 03b0e31f465..531da0f55b0 100644 --- a/pkg/services/sqlstore/store.go +++ b/pkg/services/sqlstore/store.go @@ -97,7 +97,6 @@ type Store interface { SearchOrgUsers(ctx context.Context, query *models.SearchOrgUsersQuery) error RemoveOrgUser(ctx context.Context, cmd *models.RemoveOrgUserCommand) 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 GetDataSources(ctx context.Context, query *models.GetDataSourcesQuery) error GetDataSourcesByType(ctx context.Context, query *models.GetDataSourcesByTypeQuery) error