mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Split preference store (#46843)
* Split preference store * Chore: Add tests to pref * Fix preference in wire * Rename and adjust * Add pref service test * Rename methods, add tests * Rename Preferences to Preference, names IDs correctly * Fix lint * Refactor Save * Refactor upsert Add new logic for QueryHistory Rename some fields according to go naming conventions Refactore tests * Roll back ID that breaks tests * Rename Id to ID in UpdatePreferenceQuery * Use preference as a model to modify store * Move pref store fakes to pref test file * Add integration tag for store tests * Adjust test Co-authored-by: yangkb09 <yangkb09@gmail.com>
This commit is contained in:
parent
060ccacbf9
commit
ecd6cd4a92
@ -65,13 +65,13 @@ import (
|
||||
plugindashboardsservice "github.com/grafana/grafana/pkg/services/plugindashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsettings"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/preference/prefimpl"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
"github.com/grafana/grafana/pkg/services/queryhistory"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/services/schemaloader"
|
||||
"github.com/grafana/grafana/pkg/services/search"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
"github.com/grafana/grafana/pkg/services/secrets"
|
||||
secretsDatabase "github.com/grafana/grafana/pkg/services/secrets/database"
|
||||
@ -235,6 +235,7 @@ var wireBasicSet = wire.NewSet(
|
||||
wire.Bind(new(alerting.DashAlertExtractor), new(*alerting.DashAlertExtractorService)),
|
||||
comments.ProvideService,
|
||||
guardian.ProvideService,
|
||||
prefimpl.ProvideService,
|
||||
avatar.ProvideAvatarCacheServer,
|
||||
authproxy.ProvideAuthProxy,
|
||||
statscollector.ProvideService,
|
||||
|
100
pkg/services/preference/model.go
Normal file
100
pkg/services/preference/model.go
Normal file
@ -0,0 +1,100 @@
|
||||
package pref
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrPrefNotFound = errors.New("preference not found")
|
||||
|
||||
type Preference struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
UserID int64 `xorm:"user_id"`
|
||||
TeamID int64 `xorm:"team_id"`
|
||||
Teams []int64 `xorm:"extends"`
|
||||
Version int
|
||||
HomeDashboardID int64 `xorm:"home_dashboard_id"`
|
||||
Timezone string
|
||||
WeekStart string
|
||||
Theme string
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
JSONData *PreferenceJSONData `xorm:"json_data"`
|
||||
}
|
||||
|
||||
type GetPreferenceWithDefaultsQuery struct {
|
||||
Teams []int64
|
||||
OrgID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type GetPreferenceQuery struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
type SavePreferenceCommand struct {
|
||||
UserID int64
|
||||
OrgID int64
|
||||
TeamID int64
|
||||
|
||||
HomeDashboardID int64 `json:"homeDashboardId,omitempty"`
|
||||
Timezone string `json:"timezone,omitempty"`
|
||||
WeekStart string `json:"weekStart,omitempty"`
|
||||
Theme string `json:"theme,omitempty"`
|
||||
Navbar *NavbarPreference `json:"navbar,omitempty"`
|
||||
QueryHistory *QueryHistoryPreference `json:"queryHistory,omitempty"`
|
||||
}
|
||||
|
||||
type PatchPreferenceCommand struct {
|
||||
UserID int64
|
||||
OrgID int64
|
||||
TeamID int64
|
||||
|
||||
HomeDashboardID *int64 `json:"homeDashboardId,omitempty"`
|
||||
Timezone *string `json:"timezone,omitempty"`
|
||||
WeekStart *string `json:"weekStart,omitempty"`
|
||||
Theme *string `json:"theme,omitempty"`
|
||||
Navbar *NavbarPreference `json:"navbar,omitempty"`
|
||||
QueryHistory *QueryHistoryPreference `json:"queryHistory,omitempty"`
|
||||
}
|
||||
|
||||
type NavLink struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Target string `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
type NavbarPreference struct {
|
||||
SavedItems []NavLink `json:"savedItems"`
|
||||
}
|
||||
|
||||
type PreferenceJSONData struct {
|
||||
Navbar NavbarPreference `json:"navbar"`
|
||||
QueryHistory QueryHistoryPreference `json:"queryHistory"`
|
||||
}
|
||||
|
||||
type QueryHistoryPreference struct {
|
||||
HomeTab string `json:"homeTab"`
|
||||
}
|
||||
|
||||
func (j *PreferenceJSONData) FromDB(data []byte) error {
|
||||
dec := json.NewDecoder(bytes.NewBuffer(data))
|
||||
dec.UseNumber()
|
||||
return dec.Decode(j)
|
||||
}
|
||||
|
||||
func (j *PreferenceJSONData) ToDB() ([]byte, error) {
|
||||
if j == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return json.Marshal(j)
|
||||
}
|
||||
|
||||
func (p Preference) TableName() string { return "preferences" }
|
13
pkg/services/preference/pref.go
Normal file
13
pkg/services/preference/pref.go
Normal file
@ -0,0 +1,13 @@
|
||||
package pref
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
GetWithDefaults(context.Context, *GetPreferenceWithDefaultsQuery) (*Preference, error)
|
||||
Get(context.Context, *GetPreferenceQuery) (*Preference, error)
|
||||
Save(context.Context, *SavePreferenceCommand) error
|
||||
Patch(ctx context.Context, cmd *PatchPreferenceCommand) error
|
||||
GetDefaults() *Preference
|
||||
}
|
203
pkg/services/preference/prefimpl/pref.go
Normal file
203
pkg/services/preference/prefimpl/pref.go
Normal file
@ -0,0 +1,203 @@
|
||||
package prefimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
store store
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func ProvideService(db db.DB, cfg *setting.Cfg) *Service {
|
||||
return &Service{
|
||||
store: &sqlStore{
|
||||
db: db,
|
||||
},
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
|
||||
listQuery := &pref.Preference{
|
||||
Teams: query.Teams,
|
||||
OrgID: query.OrgID,
|
||||
UserID: query.UserID,
|
||||
}
|
||||
prefs, err := s.store.List(ctx, listQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := s.GetDefaults()
|
||||
for _, p := range prefs {
|
||||
if p.Theme != "" {
|
||||
res.Theme = p.Theme
|
||||
}
|
||||
if p.Timezone != "" {
|
||||
res.Timezone = p.Timezone
|
||||
}
|
||||
if p.WeekStart != "" {
|
||||
res.WeekStart = p.WeekStart
|
||||
}
|
||||
if p.HomeDashboardID != 0 {
|
||||
res.HomeDashboardID = p.HomeDashboardID
|
||||
}
|
||||
if p.JSONData != nil {
|
||||
res.JSONData = p.JSONData
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, query *pref.GetPreferenceQuery) (*pref.Preference, error) {
|
||||
getPref := &pref.Preference{
|
||||
OrgID: query.OrgID,
|
||||
UserID: query.UserID,
|
||||
TeamID: query.TeamID,
|
||||
}
|
||||
prefs, err := s.store.Get(ctx, getPref)
|
||||
if err != nil && !errors.Is(err, pref.ErrPrefNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
return prefs, nil
|
||||
}
|
||||
|
||||
func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) error {
|
||||
preference, err := s.store.Get(ctx, &pref.Preference{
|
||||
OrgID: cmd.OrgID,
|
||||
UserID: cmd.UserID,
|
||||
TeamID: cmd.TeamID,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, pref.ErrPrefNotFound) {
|
||||
preference := &pref.Preference{
|
||||
UserID: cmd.UserID,
|
||||
OrgID: cmd.OrgID,
|
||||
TeamID: cmd.TeamID,
|
||||
HomeDashboardID: cmd.HomeDashboardID,
|
||||
Timezone: cmd.Timezone,
|
||||
WeekStart: cmd.WeekStart,
|
||||
Theme: cmd.Theme,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
_, err = s.store.Insert(ctx, preference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
preference.Timezone = cmd.Timezone
|
||||
preference.WeekStart = cmd.WeekStart
|
||||
preference.Theme = cmd.Theme
|
||||
preference.Updated = time.Now()
|
||||
preference.Version += 1
|
||||
preference.JSONData = &pref.PreferenceJSONData{}
|
||||
|
||||
if cmd.Navbar != nil {
|
||||
preference.JSONData.Navbar = *cmd.Navbar
|
||||
}
|
||||
if cmd.QueryHistory != nil {
|
||||
preference.JSONData.QueryHistory = *cmd.QueryHistory
|
||||
}
|
||||
return s.store.Update(ctx, preference)
|
||||
}
|
||||
|
||||
func (s *Service) Patch(ctx context.Context, cmd *pref.PatchPreferenceCommand) error {
|
||||
var exists bool
|
||||
preference, err := s.store.Get(ctx, &pref.Preference{
|
||||
OrgID: cmd.OrgID,
|
||||
UserID: cmd.UserID,
|
||||
TeamID: cmd.TeamID,
|
||||
})
|
||||
if err != nil && !errors.Is(err, pref.ErrPrefNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
if errors.Is(err, pref.ErrPrefNotFound) {
|
||||
preference = &pref.Preference{
|
||||
UserID: cmd.UserID,
|
||||
OrgID: cmd.OrgID,
|
||||
TeamID: cmd.TeamID,
|
||||
Created: time.Now(),
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
} else {
|
||||
exists = true
|
||||
}
|
||||
|
||||
if cmd.Navbar != nil {
|
||||
if preference.JSONData == nil {
|
||||
preference.JSONData = &pref.PreferenceJSONData{}
|
||||
}
|
||||
if cmd.Navbar.SavedItems != nil {
|
||||
preference.JSONData.Navbar.SavedItems = cmd.Navbar.SavedItems
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.QueryHistory != nil {
|
||||
if preference.JSONData == nil {
|
||||
preference.JSONData = &pref.PreferenceJSONData{}
|
||||
}
|
||||
if cmd.QueryHistory.HomeTab != "" {
|
||||
preference.JSONData.QueryHistory.HomeTab = cmd.QueryHistory.HomeTab
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.HomeDashboardID != nil {
|
||||
preference.HomeDashboardID = *cmd.HomeDashboardID
|
||||
}
|
||||
|
||||
if cmd.Timezone != nil {
|
||||
preference.Timezone = *cmd.Timezone
|
||||
}
|
||||
|
||||
if cmd.WeekStart != nil {
|
||||
preference.WeekStart = *cmd.WeekStart
|
||||
}
|
||||
|
||||
if cmd.Theme != nil {
|
||||
preference.Theme = *cmd.Theme
|
||||
}
|
||||
|
||||
preference.Updated = time.Now()
|
||||
preference.Version += 1
|
||||
|
||||
// Wrap this in an if statement to maintain backwards compatibility
|
||||
if cmd.Navbar != nil {
|
||||
if preference.JSONData == nil {
|
||||
preference.JSONData = &pref.PreferenceJSONData{}
|
||||
}
|
||||
if cmd.Navbar.SavedItems != nil {
|
||||
preference.JSONData.Navbar.SavedItems = cmd.Navbar.SavedItems
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
err = s.store.Update(ctx, preference)
|
||||
} else {
|
||||
_, err = s.store.Insert(ctx, preference)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Service) GetDefaults() *pref.Preference {
|
||||
defaults := &pref.Preference{
|
||||
Theme: s.cfg.DefaultTheme,
|
||||
Timezone: s.cfg.DateFormats.DefaultTimezone,
|
||||
WeekStart: s.cfg.DateFormats.DefaultWeekStart,
|
||||
HomeDashboardID: 0,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
|
||||
return defaults
|
||||
}
|
446
pkg/services/preference/prefimpl/pref_test.go
Normal file
446
pkg/services/preference/prefimpl/pref_test.go
Normal file
@ -0,0 +1,446 @@
|
||||
package prefimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPreferencesService(t *testing.T) {
|
||||
prefStoreFake := newPreferenceStoreFake()
|
||||
prefService := &Service{
|
||||
store: prefStoreFake,
|
||||
}
|
||||
|
||||
emptyNavbarPreferences := pref.NavbarPreference{}
|
||||
userNavbarPreferences := pref.NavbarPreference{
|
||||
SavedItems: []pref.NavLink{{
|
||||
ID: "explore",
|
||||
Text: "Explore",
|
||||
Url: "/explore",
|
||||
}},
|
||||
}
|
||||
orgNavbarPreferences := pref.NavbarPreference{
|
||||
SavedItems: []pref.NavLink{{
|
||||
ID: "alerting",
|
||||
Text: "Alerting",
|
||||
Url: "/alerting",
|
||||
}},
|
||||
}
|
||||
team1NavbarPreferences := pref.NavbarPreference{
|
||||
SavedItems: []pref.NavLink{{
|
||||
ID: "dashboards",
|
||||
Text: "Dashboards",
|
||||
Url: "/dashboards",
|
||||
}},
|
||||
}
|
||||
team2NavbarPreferences := pref.NavbarPreference{
|
||||
SavedItems: []pref.NavLink{{
|
||||
ID: "home",
|
||||
Text: "Home",
|
||||
Url: "/home",
|
||||
}},
|
||||
}
|
||||
|
||||
emptyQueryPreference := pref.QueryHistoryPreference{}
|
||||
|
||||
queryPreference := pref.QueryHistoryPreference{
|
||||
HomeTab: "hometab",
|
||||
}
|
||||
|
||||
queryPreference2 := pref.QueryHistoryPreference{
|
||||
HomeTab: "hometab",
|
||||
}
|
||||
|
||||
emptyPreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: emptyNavbarPreferences,
|
||||
}
|
||||
userPreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: userNavbarPreferences,
|
||||
QueryHistory: queryPreference,
|
||||
}
|
||||
orgPreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: orgNavbarPreferences,
|
||||
}
|
||||
team2PreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: team2NavbarPreferences,
|
||||
}
|
||||
team1PreferencesJsonData := pref.PreferenceJSONData{
|
||||
Navbar: team1NavbarPreferences,
|
||||
}
|
||||
|
||||
t.Run("GetDefaults should return defaults", func(t *testing.T) {
|
||||
prefService.cfg = setting.NewCfg()
|
||||
prefService.cfg.DefaultTheme = "light"
|
||||
prefService.cfg.DateFormats.DefaultTimezone = "UTC"
|
||||
|
||||
preferences := prefService.GetDefaults()
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "UTC",
|
||||
HomeDashboardID: 0,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preferences); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetDefaults with no saved preferences should return defaults", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "UTC",
|
||||
}
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{}
|
||||
preferences, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "UTC",
|
||||
HomeDashboardID: 0,
|
||||
JSONData: &emptyPreferencesJsonData,
|
||||
}
|
||||
if diff := cmp.Diff(expected, preferences); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetWithDefaults with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{}
|
||||
prefStoreFake.ExpectedListPreferences = []*pref.Preference{
|
||||
{
|
||||
OrgID: 1,
|
||||
HomeDashboardID: 1,
|
||||
Theme: "dark",
|
||||
Timezone: "UTC",
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
HomeDashboardID: 4,
|
||||
Theme: "light",
|
||||
WeekStart: "1",
|
||||
},
|
||||
}
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 1}
|
||||
preferences, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "UTC",
|
||||
WeekStart: "1",
|
||||
HomeDashboardID: 4,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preferences); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetWithDefaults with saved org and other user home dashboard should return org home dashboard", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{}
|
||||
prefStoreFake.ExpectedListPreferences = []*pref.Preference{
|
||||
{
|
||||
OrgID: 1,
|
||||
HomeDashboardID: 1,
|
||||
Theme: "dark",
|
||||
Timezone: "UTC",
|
||||
WeekStart: "1",
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
HomeDashboardID: 4,
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
},
|
||||
}
|
||||
prefService.GetDefaults().HomeDashboardID = 1
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 2}
|
||||
preferences, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
HomeDashboardID: 4,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preferences); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetPreferencesWithDefaults with saved org and user json data should return user json data", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{}
|
||||
prefStoreFake.ExpectedListPreferences = []*pref.Preference{
|
||||
{
|
||||
OrgID: 1,
|
||||
JSONData: &orgPreferencesJsonData,
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
JSONData: &userPreferencesJsonData,
|
||||
},
|
||||
}
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 1}
|
||||
preference, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &pref.Preference{
|
||||
Theme: "light",
|
||||
JSONData: &userPreferencesJsonData,
|
||||
Timezone: "UTC",
|
||||
}, preference)
|
||||
})
|
||||
|
||||
t.Run("GetWithDefaults with saved org and teams json data should return last team json data", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{}
|
||||
prefStoreFake.ExpectedListPreferences = []*pref.Preference{
|
||||
{
|
||||
OrgID: 1,
|
||||
JSONData: &orgPreferencesJsonData,
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
TeamID: 2,
|
||||
JSONData: &team1PreferencesJsonData,
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
TeamID: 3,
|
||||
JSONData: &team2PreferencesJsonData,
|
||||
},
|
||||
}
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{
|
||||
OrgID: 1, Teams: []int64{2, 3},
|
||||
}
|
||||
preference, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, &pref.Preference{
|
||||
Timezone: "UTC",
|
||||
Theme: "light",
|
||||
JSONData: &team2PreferencesJsonData,
|
||||
}, preference)
|
||||
})
|
||||
|
||||
t.Run("GetWithDefaults with saved org and teams home dashboard should return last team home dashboard", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
Theme: "dark",
|
||||
Timezone: "UTC",
|
||||
}
|
||||
prefStoreFake.ExpectedListPreferences = []*pref.Preference{
|
||||
{
|
||||
OrgID: 1,
|
||||
HomeDashboardID: 1,
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
},
|
||||
{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
HomeDashboardID: 4,
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
},
|
||||
}
|
||||
|
||||
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, Teams: []int64{2, 3}}
|
||||
preferences, err := prefService.GetWithDefaults(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
expected := &pref.Preference{
|
||||
Theme: "light",
|
||||
Timezone: "browser",
|
||||
WeekStart: "2",
|
||||
HomeDashboardID: 4,
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
}
|
||||
if diff := cmp.Diff(expected, preferences); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SavePreferences for a user should store correct values", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
UserID: 3,
|
||||
TeamID: 6,
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
}
|
||||
err := prefService.Save(context.Background(),
|
||||
&pref.SavePreferenceCommand{
|
||||
Theme: "dark",
|
||||
Timezone: "browser",
|
||||
HomeDashboardID: 5,
|
||||
WeekStart: "1"},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("SavePreferences for a user should store correct values, when preference not found", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedGetError = pref.ErrPrefNotFound
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
UserID: 3,
|
||||
TeamID: 6,
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
}
|
||||
err := prefService.Save(context.Background(),
|
||||
&pref.SavePreferenceCommand{
|
||||
Theme: "dark",
|
||||
Timezone: "browser",
|
||||
HomeDashboardID: 5,
|
||||
WeekStart: "1",
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
prefStoreFake.ExpectedGetError = nil
|
||||
})
|
||||
|
||||
t.Run("SavePreferences for a user should store correct values with nav and query history", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
ID: 1,
|
||||
OrgID: 1,
|
||||
UserID: 3,
|
||||
TeamID: 6,
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
JSONData: &userPreferencesJsonData,
|
||||
}
|
||||
err := prefService.Save(context.Background(),
|
||||
&pref.SavePreferenceCommand{
|
||||
Theme: "dark",
|
||||
Timezone: "browser",
|
||||
HomeDashboardID: 5,
|
||||
WeekStart: "1",
|
||||
Navbar: &userNavbarPreferences,
|
||||
QueryHistory: &emptyQueryPreference,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Get for a user should store correct values", func(t *testing.T) {
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
}
|
||||
preference, err := prefService.Get(context.Background(), &pref.GetPreferenceQuery{})
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &pref.Preference{
|
||||
ID: preference.ID,
|
||||
Version: preference.Version,
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
Created: preference.Created,
|
||||
Updated: preference.Updated,
|
||||
}
|
||||
if diff := cmp.Diff(expected, preference); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Patch for a user should store correct values", func(t *testing.T) {
|
||||
darkTheme := "dark"
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
JSONData: &userPreferencesJsonData,
|
||||
}
|
||||
err := prefService.Patch(context.Background(),
|
||||
&pref.PatchPreferenceCommand{
|
||||
Theme: &darkTheme,
|
||||
Navbar: &userNavbarPreferences,
|
||||
QueryHistory: &queryPreference2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Patch for a user should store correct values, without navbar and query history", func(t *testing.T) {
|
||||
darkTheme := "dark"
|
||||
prefStoreFake.ExpectedPreference = &pref.Preference{
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
}
|
||||
err := prefService.Patch(context.Background(),
|
||||
&pref.PatchPreferenceCommand{
|
||||
Theme: &darkTheme,
|
||||
Navbar: &userNavbarPreferences,
|
||||
QueryHistory: &queryPreference2,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Patch for a user should store correct values, when preference not found", func(t *testing.T) {
|
||||
timezone := "browser"
|
||||
weekStart := "1"
|
||||
homeDashboardID := int64(5)
|
||||
prefStoreFake.ExpectedGetError = pref.ErrPrefNotFound
|
||||
prefStoreFake.ExpectedPreference = nil
|
||||
|
||||
err := prefService.Patch(context.Background(),
|
||||
&pref.PatchPreferenceCommand{
|
||||
HomeDashboardID: &homeDashboardID,
|
||||
Timezone: &timezone,
|
||||
WeekStart: &weekStart,
|
||||
Navbar: &emptyNavbarPreferences,
|
||||
QueryHistory: &emptyQueryPreference,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
prefStoreFake.ExpectedGetError = nil
|
||||
})
|
||||
}
|
||||
|
||||
type FakePreferenceStore struct {
|
||||
ExpectedPreference *pref.Preference
|
||||
ExpectedListPreferences []*pref.Preference
|
||||
ExpectedID int64
|
||||
ExpectedError error
|
||||
ExpectedGetError error
|
||||
}
|
||||
|
||||
func newPreferenceStoreFake() *FakePreferenceStore {
|
||||
return &FakePreferenceStore{}
|
||||
}
|
||||
|
||||
func (f *FakePreferenceStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
|
||||
return f.ExpectedListPreferences, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
|
||||
return f.ExpectedPreference, f.ExpectedGetError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
|
||||
return f.ExpectedID, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceStore) Update(ctx context.Context, cmd *pref.Preference) error {
|
||||
return f.ExpectedError
|
||||
}
|
88
pkg/services/preference/prefimpl/store.go
Normal file
88
pkg/services/preference/prefimpl/store.go
Normal file
@ -0,0 +1,88 @@
|
||||
package prefimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
)
|
||||
|
||||
type store interface {
|
||||
Get(context.Context, *pref.Preference) (*pref.Preference, error)
|
||||
List(context.Context, *pref.Preference) ([]*pref.Preference, error)
|
||||
Insert(context.Context, *pref.Preference) (int64, error)
|
||||
Update(context.Context, *pref.Preference) error
|
||||
}
|
||||
|
||||
type sqlStore struct {
|
||||
db db.DB
|
||||
}
|
||||
|
||||
func (s *sqlStore) Get(ctx context.Context, query *pref.Preference) (*pref.Preference, error) {
|
||||
var prefs pref.Preference
|
||||
err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
exist, err := sess.Where("org_id=? AND user_id=? AND team_id=?", query.OrgID, query.UserID, query.TeamID).Get(&prefs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return pref.ErrPrefNotFound
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &prefs, nil
|
||||
}
|
||||
|
||||
func (s *sqlStore) List(ctx context.Context, query *pref.Preference) ([]*pref.Preference, error) {
|
||||
prefs := make([]*pref.Preference, 0)
|
||||
params := make([]interface{}, 0)
|
||||
filter := ""
|
||||
|
||||
if len(query.Teams) > 0 {
|
||||
filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.Teams)-1) + ")) OR "
|
||||
params = append(params, query.OrgID)
|
||||
for _, v := range query.Teams {
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
|
||||
filter += "(org_id=? AND user_id=? AND team_id=0) OR (org_id=? AND team_id=0 AND user_id=0)"
|
||||
params = append(params, query.OrgID)
|
||||
params = append(params, query.UserID)
|
||||
params = append(params, query.OrgID)
|
||||
|
||||
err := s.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||
err := dbSession.Where(filter, params...).
|
||||
OrderBy("user_id ASC, team_id ASC").
|
||||
Find(&prefs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return prefs, err
|
||||
}
|
||||
|
||||
func (s *sqlStore) Update(ctx context.Context, cmd *pref.Preference) error {
|
||||
return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
_, err := sess.ID(cmd.ID).AllCols().Update(cmd)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sqlStore) Insert(ctx context.Context, cmd *pref.Preference) (int64, error) {
|
||||
var ID int64
|
||||
var err error
|
||||
err = s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
ID, err = sess.Insert(cmd)
|
||||
return err
|
||||
})
|
||||
return ID, err
|
||||
}
|
170
pkg/services/preference/prefimpl/store_test.go
Normal file
170
pkg/services/preference/prefimpl/store_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package prefimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPreferencesDataAccess(t *testing.T) {
|
||||
ss := sqlstore.InitTestDB(t)
|
||||
prefStore := sqlStore{db: ss}
|
||||
orgNavbarPreferences := pref.NavbarPreference{
|
||||
SavedItems: []pref.NavLink{{
|
||||
ID: "alerting",
|
||||
Text: "Alerting",
|
||||
Url: "/alerting",
|
||||
}},
|
||||
}
|
||||
|
||||
t.Run("Get with saved org and user home dashboard returns not found", func(t *testing.T) {
|
||||
query := &pref.Preference{OrgID: 1, UserID: 1, TeamID: 2}
|
||||
prefs, err := prefStore.Get(context.Background(), query)
|
||||
require.EqualError(t, err, pref.ErrPrefNotFound.Error())
|
||||
require.Nil(t, prefs)
|
||||
})
|
||||
|
||||
t.Run("Get with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
|
||||
_, err := prefStore.Insert(context.Background(),
|
||||
&pref.Preference{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
HomeDashboardID: 4,
|
||||
TeamID: 2,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &pref.Preference{OrgID: 1, UserID: 1, TeamID: 2}
|
||||
prefs, err := prefStore.Get(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(4), prefs.HomeDashboardID)
|
||||
})
|
||||
|
||||
t.Run("List with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
|
||||
_, err := prefStore.Insert(context.Background(),
|
||||
&pref.Preference{
|
||||
OrgID: 1,
|
||||
UserID: 1,
|
||||
TeamID: 3,
|
||||
HomeDashboardID: 1,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &pref.Preference{OrgID: 1, UserID: 1, Teams: []int64{2}}
|
||||
prefs, err := prefStore.List(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(4), prefs[0].HomeDashboardID)
|
||||
})
|
||||
|
||||
t.Run("List with saved org and other user home dashboard should return org home dashboard", func(t *testing.T) {
|
||||
_, err := prefStore.Insert(context.Background(),
|
||||
&pref.Preference{
|
||||
OrgID: 1,
|
||||
UserID: 2,
|
||||
TeamID: 3,
|
||||
HomeDashboardID: 1,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &pref.Preference{OrgID: 1, UserID: 1, Teams: []int64{3}}
|
||||
prefs, err := prefStore.List(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), prefs[0].HomeDashboardID)
|
||||
require.Equal(t, int64(1), prefs[1].HomeDashboardID)
|
||||
})
|
||||
|
||||
t.Run("List with saved org and teams home dashboard should return last team home dashboard", func(t *testing.T) {
|
||||
query := &pref.Preference{
|
||||
OrgID: 1, Teams: []int64{2, 3},
|
||||
}
|
||||
prefs, err := prefStore.List(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(4), prefs[0].HomeDashboardID)
|
||||
require.Equal(t, int64(1), prefs[1].HomeDashboardID)
|
||||
require.Equal(t, int64(1), prefs[2].HomeDashboardID)
|
||||
})
|
||||
|
||||
t.Run("List with saved org and other teams home dashboard should return org home dashboard", func(t *testing.T) {
|
||||
_, err := prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, HomeDashboardID: 1, Created: time.Now(), Updated: time.Now()})
|
||||
require.NoError(t, err)
|
||||
_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 2, HomeDashboardID: 2, Created: time.Now(), Updated: time.Now()})
|
||||
require.NoError(t, err)
|
||||
_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 3, HomeDashboardID: 3, Created: time.Now(), Updated: time.Now()})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &pref.Preference{OrgID: 1}
|
||||
prefs, err := prefStore.List(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), prefs[0].HomeDashboardID)
|
||||
})
|
||||
|
||||
t.Run("Update for a user should only modify a single value", func(t *testing.T) {
|
||||
ss := sqlstore.InitTestDB(t)
|
||||
prefStore := sqlStore{db: ss}
|
||||
id, err := prefStore.Insert(context.Background(), &pref.Preference{
|
||||
UserID: models.SignedInUser{}.UserId,
|
||||
Theme: "dark",
|
||||
Timezone: "browser",
|
||||
HomeDashboardID: 5,
|
||||
WeekStart: "1",
|
||||
JSONData: &pref.PreferenceJSONData{Navbar: orgNavbarPreferences},
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = prefStore.Update(context.Background(), &pref.Preference{
|
||||
ID: id,
|
||||
Theme: "dark",
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
query := &pref.Preference{}
|
||||
prefs, err := prefStore.List(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
expected := &pref.Preference{
|
||||
ID: prefs[0].ID,
|
||||
Version: prefs[0].Version,
|
||||
HomeDashboardID: 5,
|
||||
Timezone: "browser",
|
||||
WeekStart: "1",
|
||||
Theme: "dark",
|
||||
JSONData: prefs[0].JSONData,
|
||||
Created: prefs[0].Created,
|
||||
Updated: prefs[0].Updated,
|
||||
}
|
||||
if diff := cmp.Diff(expected, prefs[0]); diff != "" {
|
||||
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
t.Run("insert preference that does not exist", func(t *testing.T) {
|
||||
_, err := prefStore.Insert(context.Background(),
|
||||
&pref.Preference{
|
||||
UserID: models.SignedInUser{}.UserId,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
JSONData: &pref.PreferenceJSONData{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
32
pkg/services/preference/preftest/fake.go
Normal file
32
pkg/services/preference/preftest/fake.go
Normal file
@ -0,0 +1,32 @@
|
||||
package preftest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
)
|
||||
|
||||
type FakePreferenceService struct {
|
||||
ExpectedPreference *pref.Preference
|
||||
ExpectedError error
|
||||
}
|
||||
|
||||
func NewPreferenceServiceFake() *FakePreferenceService {
|
||||
return &FakePreferenceService{}
|
||||
}
|
||||
|
||||
func (f *FakePreferenceService) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
|
||||
return f.ExpectedPreference, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceService) Get(ctx context.Context, query *pref.GetPreferenceQuery) (*pref.Preference, error) {
|
||||
return f.ExpectedPreference, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceService) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) error {
|
||||
return f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePreferenceService) GetDefaults() *pref.Preference {
|
||||
return f.ExpectedPreference
|
||||
}
|
Loading…
Reference in New Issue
Block a user