From c7212643c2babadb15d0164e6a9f02803b77eae6 Mon Sep 17 00:00:00 2001 From: ying-jeanne <74549700+ying-jeanne@users.noreply.github.com> Date: Wed, 17 Aug 2022 11:17:23 -0500 Subject: [PATCH] add sqlx_store into star service (#53430) --- .../dashboards/database/database_test.go | 2 +- pkg/services/sqlstore/session/session.go | 5 ++ pkg/services/star/model.go | 6 +- pkg/services/star/starimpl/sqlx_store.go | 62 ++++++++++++++++ pkg/services/star/starimpl/sqlx_store_test.go | 13 ++++ pkg/services/star/starimpl/star.go | 10 ++- pkg/services/star/starimpl/store.go | 63 ----------------- pkg/services/star/starimpl/store_test.go | 38 ++++++++-- pkg/services/star/starimpl/xorm_store.go | 70 +++++++++++++++++++ pkg/services/star/starimpl/xorm_store_test.go | 13 ++++ 10 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 pkg/services/star/starimpl/sqlx_store.go create mode 100644 pkg/services/star/starimpl/sqlx_store_test.go create mode 100644 pkg/services/star/starimpl/xorm_store.go create mode 100644 pkg/services/star/starimpl/xorm_store_test.go diff --git a/pkg/services/dashboards/database/database_test.go b/pkg/services/dashboards/database/database_test.go index 1810d8598bd..5a34a571d67 100644 --- a/pkg/services/dashboards/database/database_test.go +++ b/pkg/services/dashboards/database/database_test.go @@ -33,7 +33,7 @@ func TestIntegrationDashboardDataAccess(t *testing.T) { setup := func() { sqlStore = sqlstore.InitTestDB(t) - starService = starimpl.ProvideService(sqlStore) + starService = starimpl.ProvideService(sqlStore, sqlStore.Cfg) dashboardStore = ProvideDashboardStore(sqlStore, testFeatureToggles) savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, true, "prod", "webapp") savedDash = insertTestDashboard(t, dashboardStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp") diff --git a/pkg/services/sqlstore/session/session.go b/pkg/services/sqlstore/session/session.go index fdadf5f55c8..3f548693d2d 100644 --- a/pkg/services/sqlstore/session/session.go +++ b/pkg/services/sqlstore/session/session.go @@ -11,6 +11,7 @@ import ( type Session interface { Get(ctx context.Context, dest interface{}, query string, args ...interface{}) error Exec(ctx context.Context, query string, args ...interface{}) (sql.Result, error) + NamedExec(ctx context.Context, query string, arg interface{}) (sql.Result, error) } type SessionDB struct { @@ -33,6 +34,10 @@ func (gs *SessionDB) Exec(ctx context.Context, query string, args ...interface{} return gs.sqlxdb.ExecContext(ctx, gs.sqlxdb.Rebind(query), args...) } +func (gs *SessionDB) NamedExec(ctx context.Context, query string, arg interface{}) (sql.Result, error) { + return gs.sqlxdb.NamedExecContext(ctx, gs.sqlxdb.Rebind(query), arg) +} + func (gs *SessionDB) driverName() string { return gs.sqlxdb.DriverName() } diff --git a/pkg/services/star/model.go b/pkg/services/star/model.go index cf8e3aa882b..81cb64b12e7 100644 --- a/pkg/services/star/model.go +++ b/pkg/services/star/model.go @@ -5,9 +5,9 @@ import "errors" var ErrCommandValidationFailed = errors.New("command missing required fields") type Star struct { - ID int64 `xorm:"pk autoincr 'id'"` - UserID int64 `xorm:"user_id"` - DashboardID int64 `xorm:"dashboard_id"` + ID int64 `xorm:"pk autoincr 'id'" db:"id"` + UserID int64 `xorm:"user_id" db:"user_id"` + DashboardID int64 `xorm:"dashboard_id" db:"dashboard_id"` } // ---------------------- diff --git a/pkg/services/star/starimpl/sqlx_store.go b/pkg/services/star/starimpl/sqlx_store.go new file mode 100644 index 00000000000..c953eda7476 --- /dev/null +++ b/pkg/services/star/starimpl/sqlx_store.go @@ -0,0 +1,62 @@ +package starimpl + +import ( + "context" + "database/sql" + "errors" + + "github.com/grafana/grafana/pkg/services/sqlstore/session" + "github.com/grafana/grafana/pkg/services/star" +) + +type sqlxStore struct { + sess *session.SessionDB +} + +func (s *sqlxStore) Get(ctx context.Context, query *star.IsStarredByUserQuery) (bool, error) { + var star_res star.Star + err := s.sess.Get(ctx, &star_res, "SELECT * from star where user_id=? and dashboard_id=?", query.UserID, query.DashboardID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return false, err + } + return true, nil +} + +func (s *sqlxStore) Insert(ctx context.Context, cmd *star.StarDashboardCommand) error { + entity := star.Star{ + UserID: cmd.UserID, + DashboardID: cmd.DashboardID, + } + _, err := s.sess.NamedExec(ctx, `INSERT INTO star (user_id, dashboard_id) VALUES (:user_id, :dashboard_id)`, entity) + if err != nil { + return err + } + return err +} + +func (s *sqlxStore) Delete(ctx context.Context, cmd *star.UnstarDashboardCommand) error { + _, err := s.sess.Exec(ctx, "DELETE FROM star WHERE user_id=? and dashboard_id=?", cmd.UserID, cmd.DashboardID) + return err +} + +func (s *sqlxStore) DeleteByUser(ctx context.Context, userID int64) error { + _, err := s.sess.Exec(ctx, "DELETE FROM star WHERE user_id = ?", userID) + return err +} + +func (s *sqlxStore) List(ctx context.Context, query *star.GetUserStarsQuery) (*star.GetUserStarsResult, error) { + userStars := make(map[int64]bool) + var stars = make([]star.Star, 0) + err := s.sess.Select(ctx, &stars, "SELECT * FROM star WHERE user_id=?", query.UserID) + if err != nil { + return nil, err + } + for _, star := range stars { + userStars[star.DashboardID] = true + } + + return &star.GetUserStarsResult{UserStars: userStars}, err +} diff --git a/pkg/services/star/starimpl/sqlx_store_test.go b/pkg/services/star/starimpl/sqlx_store_test.go new file mode 100644 index 00000000000..6d1b963ec4b --- /dev/null +++ b/pkg/services/star/starimpl/sqlx_store_test.go @@ -0,0 +1,13 @@ +package starimpl + +import ( + "testing" + + "github.com/grafana/grafana/pkg/services/sqlstore" +) + +func TestIntegrationSQLxUserStarsDataAccess(t *testing.T) { + testIntegrationUserStarsDataAccess(t, func(ss *sqlstore.SQLStore) store { + return &sqlxStore{sess: ss.GetSqlxSession()} + }) +} diff --git a/pkg/services/star/starimpl/star.go b/pkg/services/star/starimpl/star.go index 8d07a2b7917..b6339ddbfb3 100644 --- a/pkg/services/star/starimpl/star.go +++ b/pkg/services/star/starimpl/star.go @@ -5,13 +5,21 @@ import ( "github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/services/star" + "github.com/grafana/grafana/pkg/setting" ) type Service struct { store store } -func ProvideService(db db.DB) star.Service { +func ProvideService(db db.DB, cfg *setting.Cfg) star.Service { + if cfg.IsFeatureToggleEnabled("newDBLibrary") { + return &Service{ + store: &sqlxStore{ + sess: db.GetSqlxSession(), + }, + } + } return &Service{ store: &sqlStore{ db: db, diff --git a/pkg/services/star/starimpl/store.go b/pkg/services/star/starimpl/store.go index dbe8cd34a0c..1f359cafcb8 100644 --- a/pkg/services/star/starimpl/store.go +++ b/pkg/services/star/starimpl/store.go @@ -3,8 +3,6 @@ package starimpl import ( "context" - "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/services/sqlstore/db" "github.com/grafana/grafana/pkg/services/star" ) @@ -15,64 +13,3 @@ type store interface { DeleteByUser(context.Context, int64) error List(context.Context, *star.GetUserStarsQuery) (*star.GetUserStarsResult, error) } - -type sqlStore struct { - db db.DB -} - -func (s *sqlStore) Get(ctx context.Context, query *star.IsStarredByUserQuery) (bool, error) { - var isStarred bool - err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { - rawSQL := "SELECT 1 from star where user_id=? and dashboard_id=?" - results, err := sess.Query(rawSQL, query.UserID, query.DashboardID) - - if err != nil { - return err - } - - isStarred = len(results) != 0 - return nil - }) - return isStarred, err -} - -func (s *sqlStore) Insert(ctx context.Context, cmd *star.StarDashboardCommand) error { - return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { - entity := star.Star{ - UserID: cmd.UserID, - DashboardID: cmd.DashboardID, - } - - _, err := sess.Insert(&entity) - return err - }) -} - -func (s *sqlStore) Delete(ctx context.Context, cmd *star.UnstarDashboardCommand) error { - return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { - var rawSQL = "DELETE FROM star WHERE user_id=? and dashboard_id=?" - _, err := sess.Exec(rawSQL, cmd.UserID, cmd.DashboardID) - return err - }) -} - -func (s *sqlStore) DeleteByUser(ctx context.Context, userID int64) error { - return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { - var rawSQL = "DELETE FROM star WHERE user_id = ?" - _, err := sess.Exec(rawSQL, userID) - return err - }) -} - -func (s *sqlStore) List(ctx context.Context, query *star.GetUserStarsQuery) (*star.GetUserStarsResult, error) { - userStars := make(map[int64]bool) - err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { - var stars = make([]star.Star, 0) - err := dbSession.Where("user_id=?", query.UserID).Find(&stars) - for _, star := range stars { - userStars[star.DashboardID] = true - } - return err - }) - return &star.GetUserStarsResult{UserStars: userStars}, err -} diff --git a/pkg/services/star/starimpl/store_test.go b/pkg/services/star/starimpl/store_test.go index e243ebfe4a9..6082fa0fc14 100644 --- a/pkg/services/star/starimpl/store_test.go +++ b/pkg/services/star/starimpl/store_test.go @@ -9,13 +9,15 @@ import ( "github.com/stretchr/testify/require" ) -func TestIntegrationUserStarsDataAccess(t *testing.T) { +type getStore func(*sqlstore.SQLStore) store + +func testIntegrationUserStarsDataAccess(t *testing.T, fn getStore) { if testing.Short() { t.Skip("skipping integration test") } t.Run("Testing User Stars Data Access", func(t *testing.T) { ss := sqlstore.InitTestDB(t) - starStore := sqlStore{db: ss} + starStore := fn(ss) t.Run("Given saved star", func(t *testing.T) { cmd := star.StarDashboardCommand{ @@ -25,14 +27,14 @@ func TestIntegrationUserStarsDataAccess(t *testing.T) { err := starStore.Insert(context.Background(), &cmd) require.NoError(t, err) - t.Run("IsStarredByUser should return true when starred", func(t *testing.T) { + t.Run("Get should return true when starred", func(t *testing.T) { query := star.IsStarredByUserQuery{UserID: 12, DashboardID: 10} isStarred, err := starStore.Get(context.Background(), &query) require.NoError(t, err) require.True(t, isStarred) }) - t.Run("IsStarredByUser should return false when not starred", func(t *testing.T) { + t.Run("Get should return false when not starred", func(t *testing.T) { query := star.IsStarredByUserQuery{UserID: 12, DashboardID: 12} isStarred, err := starStore.Get(context.Background(), &query) require.NoError(t, err) @@ -57,9 +59,33 @@ func TestIntegrationUserStarsDataAccess(t *testing.T) { }) }) - t.Run("delete by user", func(t *testing.T) { - err := starStore.DeleteByUser(context.Background(), 1) + t.Run("DeleteByUser should remove the star for user", func(t *testing.T) { + star1 := star.StarDashboardCommand{ + DashboardID: 10, + UserID: 12, + } + err := starStore.Insert(context.Background(), &star1) require.NoError(t, err) + star2 := star.StarDashboardCommand{ + DashboardID: 11, + UserID: 12, + } + err = starStore.Insert(context.Background(), &star2) + require.NoError(t, err) + star3 := star.StarDashboardCommand{ + DashboardID: 11, + UserID: 11, + } + err = starStore.Insert(context.Background(), &star3) + require.NoError(t, err) + err = starStore.DeleteByUser(context.Background(), 12) + require.NoError(t, err) + res, err := starStore.List(context.Background(), &star.GetUserStarsQuery{UserID: 12}) + require.NoError(t, err) + require.Equal(t, 0, len(res.UserStars)) + res, err = starStore.List(context.Background(), &star.GetUserStarsQuery{UserID: 11}) + require.NoError(t, err) + require.Equal(t, 1, len(res.UserStars)) }) }) } diff --git a/pkg/services/star/starimpl/xorm_store.go b/pkg/services/star/starimpl/xorm_store.go new file mode 100644 index 00000000000..025332cd6f7 --- /dev/null +++ b/pkg/services/star/starimpl/xorm_store.go @@ -0,0 +1,70 @@ +package starimpl + +import ( + "context" + + "github.com/grafana/grafana/pkg/services/sqlstore" + "github.com/grafana/grafana/pkg/services/sqlstore/db" + "github.com/grafana/grafana/pkg/services/star" +) + +type sqlStore struct { + db db.DB +} + +func (s *sqlStore) Get(ctx context.Context, query *star.IsStarredByUserQuery) (bool, error) { + var isStarred bool + err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { + rawSQL := "SELECT 1 from star where user_id=? and dashboard_id=?" + results, err := sess.Query(rawSQL, query.UserID, query.DashboardID) + + if err != nil { + return err + } + + isStarred = len(results) != 0 + return nil + }) + return isStarred, err +} + +func (s *sqlStore) Insert(ctx context.Context, cmd *star.StarDashboardCommand) error { + return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + entity := star.Star{ + UserID: cmd.UserID, + DashboardID: cmd.DashboardID, + } + + _, err := sess.Insert(&entity) + return err + }) +} + +func (s *sqlStore) Delete(ctx context.Context, cmd *star.UnstarDashboardCommand) error { + return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + var rawSQL = "DELETE FROM star WHERE user_id=? and dashboard_id=?" + _, err := sess.Exec(rawSQL, cmd.UserID, cmd.DashboardID) + return err + }) +} + +func (s *sqlStore) DeleteByUser(ctx context.Context, userID int64) error { + return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { + var rawSQL = "DELETE FROM star WHERE user_id = ?" + _, err := sess.Exec(rawSQL, userID) + return err + }) +} + +func (s *sqlStore) List(ctx context.Context, query *star.GetUserStarsQuery) (*star.GetUserStarsResult, error) { + userStars := make(map[int64]bool) + err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { + var stars = make([]star.Star, 0) + err := dbSession.Where("user_id=?", query.UserID).Find(&stars) + for _, star := range stars { + userStars[star.DashboardID] = true + } + return err + }) + return &star.GetUserStarsResult{UserStars: userStars}, err +} diff --git a/pkg/services/star/starimpl/xorm_store_test.go b/pkg/services/star/starimpl/xorm_store_test.go new file mode 100644 index 00000000000..3b5c21cc4b3 --- /dev/null +++ b/pkg/services/star/starimpl/xorm_store_test.go @@ -0,0 +1,13 @@ +package starimpl + +import ( + "testing" + + "github.com/grafana/grafana/pkg/services/sqlstore" +) + +func TestIntegrationXormUserStarsDataAccess(t *testing.T) { + testIntegrationUserStarsDataAccess(t, func(ss *sqlstore.SQLStore) store { + return &sqlStore{db: ss} + }) +}