Preferences: Use dashboard uid for the home dashboard (#106666)

This commit is contained in:
Stephanie Hingtgen
2025-06-13 07:10:44 -05:00
committed by GitHub
parent 4c2bfe8263
commit 352aac162c
16 changed files with 303 additions and 177 deletions
@@ -21,7 +21,8 @@ title: 'Preferences API'
Keys:
- **theme** - One of: `light`, `dark`, or an empty string for the default theme
- **homeDashboardId** - The numerical `:id` of a favorited dashboard, default: `0`
- **homeDashboardId** - Deprecated. Use `homeDashboardUID` instead.
- **homeDashboardUID**: The `:uid` of a dashboard
- **timezone** - One of: `utc`, `browser`, or an empty string for the default
Omitting a key will cause the current value to be replaced with the
@@ -139,6 +140,7 @@ Content-Type: application/json
{
"theme": "",
"homeDashboardId": 0,
"homeDashboardUID": "",
"timezone": "",
"weekStart": "",
"navbar": {
+4 -1
View File
@@ -477,6 +477,7 @@ Content-Type: application/json
{
"theme": "",
"homeDashboardId": 0,
"homeDashboardUID": "",
"timezone": ""
}
```
@@ -504,6 +505,7 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"theme": "dark",
"homeDashboardId": 39,
"homeDashboardUID": "jcIIG-07z",
"timezone": "utc"
}
```
@@ -511,7 +513,8 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
JSON Body Schema:
- **theme** - One of: `light`, `dark`, or an empty string for the default theme
- **homeDashboardId** - The numerical `:id` of a dashboard, default: `0`
- **homeDashboardId** - Deprecated. Use `homeDashboardUID` instead.
- **homeDashboardUID** - The `:uid` of a dashboard
- **timezone** - One of: `utc`, `browser`, or an empty string for the default
Omitting a key will cause the current value to be replaced with the system default value.
+4 -5
View File
@@ -580,16 +580,15 @@ func (hs *HTTPServer) GetHomeDashboard(c *contextmodel.ReqContext) response.Resp
return response.Error(http.StatusInternalServerError, "Failed to get preferences", err)
}
if preference.HomeDashboardID == 0 && len(homePage) > 0 {
if preference.HomeDashboardUID == "" && len(homePage) > 0 {
homePageRedirect := dtos.DashboardRedirect{RedirectUri: homePage}
return response.JSON(http.StatusOK, &homePageRedirect)
}
if preference.HomeDashboardID != 0 {
slugQuery := dashboards.GetDashboardRefByIDQuery{ID: preference.HomeDashboardID}
slugQueryResult, err := hs.DashboardService.GetDashboardUIDByID(c.Req.Context(), &slugQuery)
if preference.HomeDashboardUID != "" {
slugQueryResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &dashboards.GetDashboardQuery{UID: preference.HomeDashboardUID, OrgID: c.GetOrgID()})
if err == nil {
url := dashboards.GetDashboardURL(slugQueryResult.UID, slugQueryResult.Slug)
url := dashboards.GetDashboardURL(preference.HomeDashboardUID, slugQueryResult.Slug)
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
return response.JSON(http.StatusOK, &dashRedirect)
}
+2
View File
@@ -10,6 +10,7 @@ type UpdatePrefsCmd struct {
Theme string `json:"theme"`
// The numerical :id of a favorited dashboard
// Default:0
// Deprecated: Use HomeDashboardUID instead
HomeDashboardID int64 `json:"homeDashboardId"`
HomeDashboardUID *string `json:"homeDashboardUID,omitempty"`
// Enum: utc,browser
@@ -28,6 +29,7 @@ type PatchPrefsCmd struct {
Theme *string `json:"theme,omitempty"`
// The numerical :id of a favorited dashboard
// Default:0
// Deprecated: Use HomeDashboardUID instead
HomeDashboardID *int64 `json:"homeDashboardId,omitempty"`
// Enum: utc,browser
Timezone *string `json:"timezone,omitempty"`
+23 -4
View File
@@ -30,8 +30,8 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
cmd.UserID = userID
cmd.OrgID = c.GetOrgID()
// the default value of HomeDashboardID is taken from input, when HomeDashboardID is set also,
// UID is used in preference to identify dashboard
// convert dashboard UID to ID in order to store internally if it exists in the query, otherwise take the id from query
// nolint:staticcheck
dashboardID := cmd.HomeDashboardID
if cmd.HomeDashboardUID != nil {
query := dashboards.GetDashboardQuery{UID: *cmd.HomeDashboardUID}
@@ -44,8 +44,16 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
}
dashboardID = queryResult.ID
}
} else if cmd.HomeDashboardID != 0 { // nolint:staticcheck
// make sure uid is always set if id is set
queryResult, err := hs.DashboardService.GetDashboard(c.Req.Context(), &dashboards.GetDashboardQuery{ID: cmd.HomeDashboardID, OrgID: cmd.OrgID}) // nolint:staticcheck
if err != nil {
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
cmd.HomeDashboardUID = &queryResult.UID
}
// nolint:staticcheck
cmd.HomeDashboardID = dashboardID
if err := hs.preferenceService.Save(c.Req.Context(), &cmd); err != nil {
@@ -76,7 +84,7 @@ func (hs *HTTPServer) GetUserPreferences(c *contextmodel.ReqContext) response.Re
//
// Update user preferences.
//
// Omitting a key (`theme`, `homeDashboardId`, `timezone`) will cause the current value to be replaced with the system default value.
// Omitting a key (`theme`, `homeDashboardUID`, `timezone`) will cause the current value to be replaced with the system default value.
//
// Responses:
// 200: okResponse
@@ -127,6 +135,7 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
}
// convert dashboard UID to ID in order to store internally if it exists in the query, otherwise take the id from query
// nolint:staticcheck
dashboardID := dtoCmd.HomeDashboardID
if dtoCmd.HomeDashboardUID != nil {
query := dashboards.GetDashboardQuery{UID: *dtoCmd.HomeDashboardUID, OrgID: orgID}
@@ -141,7 +150,16 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
}
dashboardID = &queryResult.ID
}
} else if dtoCmd.HomeDashboardID != nil {
// make sure uid is always set if id is set
queryResult, err := hs.DashboardService.GetDashboard(ctx, &dashboards.GetDashboardQuery{ID: *dtoCmd.HomeDashboardID, OrgID: orgID}) // nolint:staticcheck
if err != nil {
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
dtoCmd.HomeDashboardUID = &queryResult.UID
}
// nolint:staticcheck
dtoCmd.HomeDashboardID = dashboardID
patchCmd := pref.PatchPreferenceCommand{
@@ -151,7 +169,8 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
Theme: dtoCmd.Theme,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
HomeDashboardID: dtoCmd.HomeDashboardID, // nolint:staticcheck
HomeDashboardUID: dtoCmd.HomeDashboardUID,
Language: dtoCmd.Language,
Locale: dtoCmd.Locale,
QueryHistory: dtoCmd.QueryHistory,
+5 -5
View File
@@ -37,14 +37,9 @@ func TestAPIEndpoint_GetCurrentOrgPreferences(t *testing.T) {
prefService := preftest.NewPreferenceServiceFake()
prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"}
dashSvc := dashboards.NewFakeDashboardService(t)
qResult := &dashboards.Dashboard{UID: "home", ID: 1}
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = setting.NewCfg()
hs.preferenceService = prefService
hs.DashboardService = dashSvc
})
t.Run("AccessControl allows getting org preferences with correct permissions", func(t *testing.T) {
@@ -78,9 +73,14 @@ func TestAPIEndpoint_PutCurrentOrgPreferences(t *testing.T) {
prefService := preftest.NewPreferenceServiceFake()
prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"}
dashSvc := dashboards.NewFakeDashboardService(t)
qResult := &dashboards.Dashboard{UID: "home", ID: 1}
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = setting.NewCfg()
hs.preferenceService = prefService
hs.DashboardService = dashSvc
})
input := strings.NewReader(testUpdateOrgPreferencesCmd)
+1 -1
View File
@@ -212,7 +212,7 @@ func (s *ServiceImpl) getHomeNode(c *contextmodel.ReqContext, prefs *pref.Prefer
} else {
homePage := s.cfg.HomePage
if prefs.HomeDashboardID == 0 && len(homePage) > 0 {
if prefs.HomeDashboardUID == "" && len(homePage) > 0 {
homeUrl = homePage
}
}
+17 -13
View File
@@ -18,19 +18,21 @@ var ErrUnknownCookieType = errutil.BadRequest(
)
type Preference struct {
ID int64 `xorm:"pk autoincr 'id'" db:"id"`
OrgID int64 `xorm:"org_id" db:"org_id"`
UserID int64 `xorm:"user_id" db:"user_id"`
TeamID int64 `xorm:"team_id" db:"team_id"`
Teams []int64 `xorm:"extends"`
Version int `db:"version"`
HomeDashboardID int64 `xorm:"home_dashboard_id" db:"home_dashboard_id"`
Timezone string `db:"timezone"`
WeekStart *string `db:"week_start"`
Theme string `db:"theme"`
Created time.Time `db:"created"`
Updated time.Time `db:"updated"`
JSONData *PreferenceJSONData `xorm:"json_data" db:"json_data"`
ID int64 `xorm:"pk autoincr 'id'" db:"id"`
OrgID int64 `xorm:"org_id" db:"org_id"`
UserID int64 `xorm:"user_id" db:"user_id"`
TeamID int64 `xorm:"team_id" db:"team_id"`
Teams []int64 `xorm:"extends"`
Version int `db:"version"`
// Deprecated: Use HomeDashboardUID instead
HomeDashboardID int64 `xorm:"home_dashboard_id" db:"home_dashboard_id"`
HomeDashboardUID string `xorm:"home_dashboard_uid" db:"home_dashboard_uid"`
Timezone string `db:"timezone"`
WeekStart *string `db:"week_start"`
Theme string `db:"theme"`
Created time.Time `db:"created"`
Updated time.Time `db:"updated"`
JSONData *PreferenceJSONData `xorm:"json_data" db:"json_data"`
}
func (p Preference) Cookies(typ string) bool {
@@ -59,6 +61,7 @@ type SavePreferenceCommand struct {
OrgID int64
TeamID int64
// Deprecated: Use HomeDashboardUID instead
HomeDashboardID int64 `json:"homeDashboardId,omitempty"`
HomeDashboardUID *string `json:"homeDashboardUID,omitempty"`
Timezone string `json:"timezone,omitempty"`
@@ -76,6 +79,7 @@ type PatchPreferenceCommand struct {
OrgID int64
TeamID int64
// Deprecated: Use HomeDashboardUID instead
HomeDashboardID *int64 `json:"homeDashboardId,omitempty"`
HomeDashboardUID *string `json:"homeDashboardUID,omitempty"`
Timezone *string `json:"timezone,omitempty"`
+13 -13
View File
@@ -20,6 +20,8 @@ func UpdatePreferencesFor(ctx context.Context,
return response.Error(http.StatusBadRequest, "Invalid theme", nil)
}
// convert dashboard UID to ID in order to store internally if it exists in the query, otherwise take the id from query
// nolint:staticcheck
dashboardID := dtoCmd.HomeDashboardID
if dtoCmd.HomeDashboardUID != nil {
query := dashboards.GetDashboardQuery{UID: *dtoCmd.HomeDashboardUID, OrgID: orgID}
@@ -33,7 +35,15 @@ func UpdatePreferencesFor(ctx context.Context,
}
dashboardID = queryResult.ID
}
} else if dtoCmd.HomeDashboardID != 0 {
// make sure uid is always set if id is set
queryResult, err := dashboardService.GetDashboard(ctx, &dashboards.GetDashboardQuery{ID: dtoCmd.HomeDashboardID, OrgID: orgID}) // nolint:staticcheck
if err != nil {
return response.Error(http.StatusNotFound, "Dashboard not found", err)
}
dtoCmd.HomeDashboardUID = &queryResult.UID
}
// nolint:staticcheck
dtoCmd.HomeDashboardID = dashboardID
saveCmd := pref.SavePreferenceCommand{
@@ -45,6 +55,7 @@ func UpdatePreferencesFor(ctx context.Context,
Timezone: dtoCmd.Timezone,
WeekStart: dtoCmd.WeekStart,
HomeDashboardID: dtoCmd.HomeDashboardID,
HomeDashboardUID: dtoCmd.HomeDashboardUID,
QueryHistory: dtoCmd.QueryHistory,
CookiePreferences: dtoCmd.Cookies,
Navbar: dtoCmd.Navbar,
@@ -71,26 +82,15 @@ func GetPreferencesFor(ctx context.Context,
return response.Error(http.StatusInternalServerError, "Failed to get preferences", err)
}
var dashboardUID string
// when homedashboardID is 0, that means it is the default home dashboard, no UID would be returned in the response
if preference.HomeDashboardID != 0 {
query := dashboards.GetDashboardQuery{ID: preference.HomeDashboardID, OrgID: orgID}
queryResult, err := dashboardService.GetDashboard(ctx, &query)
if err == nil {
dashboardUID = queryResult.UID
}
}
dto := preferences.Spec{}
if preference.WeekStart != nil && *preference.WeekStart != "" {
dto.WeekStart = preference.WeekStart
}
if preference.Theme != "" {
dto.Theme = &preference.Theme
}
if dashboardUID != "" {
dto.HomeDashboardUID = &dashboardUID
if preference.HomeDashboardUID != "" {
dto.HomeDashboardUID = &preference.HomeDashboardUID
}
if preference.Timezone != "" {
dto.Timezone = &preference.Timezone
+32 -12
View File
@@ -26,10 +26,11 @@ func ProvideService(db db.DB, cfg *setting.Cfg) pref.Service {
func prefsFromConfig(cfg *setting.Cfg) pref.Preference {
return pref.Preference{
Theme: cfg.DefaultTheme,
Timezone: cfg.DateFormats.DefaultTimezone,
WeekStart: &cfg.DateFormats.DefaultWeekStart,
HomeDashboardID: 0,
Theme: cfg.DefaultTheme,
Timezone: cfg.DateFormats.DefaultTimezone,
WeekStart: &cfg.DateFormats.DefaultWeekStart,
HomeDashboardID: 0, // nolint:staticcheck
HomeDashboardUID: "",
JSONData: &pref.PreferenceJSONData{
Language: cfg.DefaultLanguage,
},
@@ -59,9 +60,13 @@ func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreference
if p.WeekStart != nil && *p.WeekStart != "" {
res.WeekStart = p.WeekStart
}
// nolint: staticcheck
if p.HomeDashboardID != 0 {
res.HomeDashboardID = p.HomeDashboardID
}
if p.HomeDashboardUID != "" {
res.HomeDashboardUID = p.HomeDashboardUID
}
if p.JSONData != nil {
if p.JSONData.Language != "" {
res.JSONData.Language = p.JSONData.Language
@@ -118,9 +123,10 @@ func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) err
if err != nil {
if errors.Is(err, pref.ErrPrefNotFound) {
preference := &pref.Preference{
UserID: cmd.UserID,
OrgID: cmd.OrgID,
TeamID: cmd.TeamID,
UserID: cmd.UserID,
OrgID: cmd.OrgID,
TeamID: cmd.TeamID,
// nolint: staticcheck
HomeDashboardID: cmd.HomeDashboardID,
Timezone: cmd.Timezone,
WeekStart: &cmd.WeekStart,
@@ -129,6 +135,11 @@ func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) err
Updated: time.Now(),
JSONData: jsonData,
}
if cmd.HomeDashboardUID != nil {
preference.HomeDashboardUID = *cmd.HomeDashboardUID
}
_, err = s.store.Insert(ctx, preference)
if err != nil {
return err
@@ -142,7 +153,10 @@ func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) err
preference.Theme = cmd.Theme
preference.Updated = time.Now()
preference.Version += 1
preference.HomeDashboardID = cmd.HomeDashboardID
preference.HomeDashboardID = cmd.HomeDashboardID // nolint:staticcheck
if cmd.HomeDashboardUID != nil {
preference.HomeDashboardUID = *cmd.HomeDashboardUID
}
preference.JSONData = jsonData
return s.store.Update(ctx, preference)
@@ -201,10 +215,15 @@ func (s *Service) Patch(ctx context.Context, cmd *pref.PatchPreferenceCommand) e
}
}
// nolint: staticcheck
if cmd.HomeDashboardID != nil {
preference.HomeDashboardID = *cmd.HomeDashboardID
}
if cmd.HomeDashboardUID != nil {
preference.HomeDashboardUID = *cmd.HomeDashboardUID
}
if cmd.CookiePreferences != nil {
cookies, err := parseCookiePreferences(cmd.CookiePreferences)
if err != nil {
@@ -242,10 +261,11 @@ func (s *Service) Patch(ctx context.Context, cmd *pref.PatchPreferenceCommand) e
func (s *Service) GetDefaults() *pref.Preference {
return &pref.Preference{
Theme: s.defaults.Theme,
Timezone: s.defaults.Timezone,
WeekStart: s.defaults.WeekStart,
HomeDashboardID: 0,
Theme: s.defaults.Theme,
Timezone: s.defaults.Timezone,
WeekStart: s.defaults.WeekStart,
HomeDashboardID: 0, // nolint:staticcheck
HomeDashboardUID: "",
JSONData: &pref.PreferenceJSONData{
Language: s.defaults.JSONData.Language,
},
+79 -62
View File
@@ -41,10 +41,11 @@ func TestGetDefaults(t *testing.T) {
t.Run("GetDefaults", func(t *testing.T) {
preference := prefService.GetDefaults()
expected := &pref.Preference{
WeekStart: &weekStart,
Theme: "light",
Timezone: "UTC",
HomeDashboardID: 0,
WeekStart: &weekStart,
Theme: "light",
Timezone: "UTC",
HomeDashboardID: 0, // nolint:staticcheck
HomeDashboardUID: "",
JSONData: &pref.PreferenceJSONData{
Language: "en-US",
},
@@ -59,10 +60,11 @@ func TestGetDefaults(t *testing.T) {
preference, err := prefService.GetWithDefaults(context.Background(), query)
require.NoError(t, err)
expected := &pref.Preference{
WeekStart: &weekStart,
Theme: "light",
Timezone: "UTC",
HomeDashboardID: 0,
WeekStart: &weekStart,
Theme: "light",
Timezone: "UTC",
HomeDashboardID: 0, // nolint:staticcheck
HomeDashboardUID: "",
JSONData: &pref.PreferenceJSONData{
Language: "en-US",
},
@@ -85,23 +87,25 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
weekStartTwo := "2"
insertPrefs(t, prefService.store,
pref.Preference{
OrgID: 1,
HomeDashboardID: 1,
Theme: "dark",
Timezone: "UTC",
WeekStart: &weekStartOne,
OrgID: 1,
HomeDashboardID: 1, // nolint:staticcheck
Theme: "dark",
Timezone: "UTC",
WeekStart: &weekStartOne,
HomeDashboardUID: "test-uid",
JSONData: &pref.PreferenceJSONData{
Language: "en-GB",
Locale: "en",
},
},
pref.Preference{
OrgID: 1,
UserID: 1,
HomeDashboardID: 4,
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
OrgID: 1,
UserID: 1,
HomeDashboardID: 4, // nolint:staticcheck
HomeDashboardUID: "test-uid4",
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
JSONData: &pref.PreferenceJSONData{
Language: "en-AU",
Locale: "es",
@@ -114,10 +118,11 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
preference, err := prefService.GetWithDefaults(context.Background(), query)
require.NoError(t, err)
expected := &pref.Preference{
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
HomeDashboardID: 4,
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
HomeDashboardID: 4, // nolint:staticcheck
HomeDashboardUID: "test-uid4",
JSONData: &pref.PreferenceJSONData{
Language: "en-AU",
Locale: "es",
@@ -129,15 +134,16 @@ func TestGetWithDefaults_withUserAndOrgPrefs(t *testing.T) {
})
t.Run("ignore other user's preferences", func(t *testing.T) {
prefService.GetDefaults().HomeDashboardID = 1
prefService.GetDefaults().HomeDashboardID = 1 // nolint:staticcheck
query := &pref.GetPreferenceWithDefaultsQuery{OrgID: 1, UserID: 2}
preference, err := prefService.GetWithDefaults(context.Background(), query)
require.NoError(t, err)
expected := &pref.Preference{
Theme: "dark",
Timezone: "UTC",
WeekStart: &weekStartOne,
HomeDashboardID: 1,
Theme: "dark",
Timezone: "UTC",
WeekStart: &weekStartOne,
HomeDashboardID: 1, // nolint:staticcheck
HomeDashboardUID: "test-uid",
JSONData: &pref.PreferenceJSONData{
Language: "en-GB",
Locale: "en",
@@ -298,27 +304,30 @@ func TestGetWithDefaults_teams(t *testing.T) {
}
insertPrefs(t, prefService.store,
pref.Preference{
OrgID: 1,
HomeDashboardID: 1,
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartOne,
OrgID: 1,
HomeDashboardID: 1, // nolint:staticcheck
HomeDashboardUID: "test-uid",
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartOne,
},
pref.Preference{
OrgID: 1,
TeamID: 2,
HomeDashboardID: 3,
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
OrgID: 1,
TeamID: 2,
HomeDashboardID: 3, // nolint:staticcheck
HomeDashboardUID: "test-uid3",
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
},
pref.Preference{
OrgID: 1,
TeamID: 3,
HomeDashboardID: 4,
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
OrgID: 1,
TeamID: 3,
HomeDashboardID: 4, // nolint:staticcheck
HomeDashboardUID: "test-uid4",
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
},
)
@@ -326,11 +335,12 @@ func TestGetWithDefaults_teams(t *testing.T) {
preferences, err := prefService.GetWithDefaults(context.Background(), query)
require.NoError(t, err)
expected := &pref.Preference{
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
HomeDashboardID: 4,
JSONData: &pref.PreferenceJSONData{},
Theme: "light",
Timezone: "browser",
WeekStart: &weekStartTwo,
HomeDashboardID: 4, // nolint:staticcheck
HomeDashboardUID: "test-uid4",
JSONData: &pref.PreferenceJSONData{},
}
if diff := cmp.Diff(expected, preferences); diff != "" {
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
@@ -364,13 +374,15 @@ func TestSave(t *testing.T) {
}
t.Run("insert", func(t *testing.T) {
testUID := "test-uid5"
err := prefService.Save(context.Background(),
&pref.SavePreferenceCommand{
OrgID: 1,
Theme: "dark",
Timezone: "browser",
HomeDashboardID: 5,
WeekStart: "1",
OrgID: 1,
Theme: "dark",
Timezone: "browser",
HomeDashboardID: 5, // nolint:staticcheck
HomeDashboardUID: &testUID,
WeekStart: "1",
},
)
require.NoError(t, err)
@@ -381,18 +393,21 @@ func TestSave(t *testing.T) {
assert.Zero(t, stored.TeamID)
assert.Equal(t, "dark", stored.Theme)
assert.Equal(t, "browser", stored.Timezone)
assert.EqualValues(t, 5, stored.HomeDashboardID)
assert.EqualValues(t, 5, stored.HomeDashboardID) // nolint:staticcheck
assert.Equal(t, testUID, stored.HomeDashboardUID)
assert.Equal(t, "1", *stored.WeekStart)
assert.EqualValues(t, 0, stored.Version)
})
t.Run("update", func(t *testing.T) {
testEmptyUID := ""
err := prefService.Save(context.Background(),
&pref.SavePreferenceCommand{
OrgID: 1,
Timezone: "UTC",
HomeDashboardID: 0,
WeekStart: "1",
OrgID: 1,
Timezone: "UTC",
HomeDashboardID: 0, // nolint:staticcheck
HomeDashboardUID: &testEmptyUID,
WeekStart: "1",
},
)
require.NoError(t, err)
@@ -403,7 +418,8 @@ func TestSave(t *testing.T) {
assert.Zero(t, stored.TeamID)
assert.Empty(t, stored.Theme)
assert.Equal(t, "UTC", stored.Timezone)
assert.Zero(t, stored.HomeDashboardID)
assert.Zero(t, stored.HomeDashboardID) // nolint:staticcheck
assert.Equal(t, "", stored.HomeDashboardUID)
assert.Equal(t, "1", *stored.WeekStart)
assert.EqualValues(t, 1, stored.Version)
})
@@ -422,7 +438,8 @@ func TestSave(t *testing.T) {
assert.Zero(t, stored.TeamID)
assert.Equal(t, themeValue, stored.Theme)
assert.Equal(t, "UTC", stored.Timezone)
assert.Zero(t, stored.HomeDashboardID)
assert.Zero(t, stored.HomeDashboardID) // nolint:staticcheck
assert.Equal(t, "", stored.HomeDashboardUID)
assert.Equal(t, "1", *stored.WeekStart)
assert.EqualValues(t, 2, stored.Version)
})
+71 -54
View File
@@ -36,56 +36,63 @@ func testIntegrationPreferencesDataAccess(t *testing.T, fn getStore) {
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(),
OrgID: 1,
UserID: 1,
HomeDashboardID: 4, // nolint:staticcheck
HomeDashboardUID: "test-uid4",
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)
require.Equal(t, int64(4), prefs.HomeDashboardID) // nolint:staticcheck
require.Equal(t, "test-uid4", prefs.HomeDashboardUID)
})
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(),
OrgID: 1,
UserID: 1,
TeamID: 3,
HomeDashboardID: 1, // nolint:staticcheck
HomeDashboardUID: "test-uid1",
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)
require.Equal(t, int64(4), prefs[0].HomeDashboardID) // nolint:staticcheck
require.Equal(t, "test-uid4", prefs[0].HomeDashboardUID)
})
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(),
OrgID: 1,
UserID: 2,
TeamID: 3,
HomeDashboardID: 1, // nolint:staticcheck
HomeDashboardUID: "test-uid1",
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)
require.Equal(t, int64(1), prefs[0].HomeDashboardID) // nolint:staticcheck
require.Equal(t, int64(1), prefs[1].HomeDashboardID) // nolint:staticcheck
require.Equal(t, "test-uid1", prefs[0].HomeDashboardUID)
require.Equal(t, "test-uid1", prefs[1].HomeDashboardUID)
})
t.Run("List with saved org and teams home dashboard should return last team home dashboard", func(t *testing.T) {
@@ -94,64 +101,74 @@ func testIntegrationPreferencesDataAccess(t *testing.T, fn getStore) {
}
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)
require.Equal(t, int64(4), prefs[0].HomeDashboardID) // nolint:staticcheck
require.Equal(t, int64(1), prefs[1].HomeDashboardID) // nolint:staticcheck
require.Equal(t, int64(1), prefs[2].HomeDashboardID) // nolint:staticcheck
require.Equal(t, "test-uid4", prefs[0].HomeDashboardUID)
require.Equal(t, "test-uid1", prefs[1].HomeDashboardUID)
require.Equal(t, "test-uid1", prefs[2].HomeDashboardUID)
})
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()})
// nolint:staticcheck
_, err := prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, HomeDashboardID: 1, HomeDashboardUID: "test-uid1", 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()})
// nolint:staticcheck
_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 2, HomeDashboardID: 2, HomeDashboardUID: "test-uid2", 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()})
// nolint:staticcheck
_, err = prefStore.Insert(context.Background(), &pref.Preference{OrgID: 1, TeamID: 3, HomeDashboardID: 3, HomeDashboardUID: "test-uid3", 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)
require.Equal(t, int64(1), prefs[0].HomeDashboardID) // nolint:staticcheck
require.Equal(t, "test-uid1", prefs[0].HomeDashboardUID)
})
t.Run("Update for a user should only modify a single value", func(t *testing.T) {
ss := db.InitTestDB(t)
prefStore := fn(ss)
id, err := prefStore.Insert(context.Background(), &pref.Preference{
UserID: user.SignedInUser{}.UserID,
Theme: "dark",
Timezone: "browser",
HomeDashboardID: 5,
WeekStart: &weekStartOne,
JSONData: &pref.PreferenceJSONData{},
Created: time.Now(),
Updated: time.Now(),
UserID: user.SignedInUser{}.UserID,
Theme: "dark",
Timezone: "browser",
HomeDashboardID: 5, // nolint:staticcheck
HomeDashboardUID: "test-uid5",
WeekStart: &weekStartOne,
JSONData: &pref.PreferenceJSONData{},
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: &weekStartOne,
Created: time.Now(),
Updated: time.Now(),
JSONData: &pref.PreferenceJSONData{},
ID: id,
Theme: "dark",
HomeDashboardID: 5, // nolint:staticcheck
HomeDashboardUID: "test-uid5",
Timezone: "browser",
WeekStart: &weekStartOne,
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: &weekStartOne,
Theme: "dark",
JSONData: prefs[0].JSONData,
Created: prefs[0].Created,
Updated: prefs[0].Updated,
ID: prefs[0].ID,
Version: prefs[0].Version,
HomeDashboardID: 5, // nolint:staticcheck
HomeDashboardUID: "test-uid5",
Timezone: "browser",
WeekStart: &weekStartOne,
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)
@@ -1,7 +1,10 @@
package migrations
import (
"fmt"
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/util/xorm"
)
func addPreferencesMigrations(mg *Migrator) {
@@ -58,4 +61,48 @@ func addPreferencesMigrations(mg *Migrator) {
mg.AddMigration("Add preferences index org_id", NewAddIndexMigration(preferencesV2, preferencesV2.Indices[0]))
mg.AddMigration("Add preferences index user_id", NewAddIndexMigration(preferencesV2, preferencesV2.Indices[1]))
mg.AddMigration("Add home_dashboard_uid column to preferences table", NewAddColumnMigration(preferencesV2, &Column{
Name: "home_dashboard_uid", Type: DB_NVarchar, Length: 40, Nullable: true,
}))
mg.AddMigration("Add missing dashboard_uid to preferences table", &AddDashboardUIDMigration{})
}
type AddDashboardUIDMigration struct {
MigrationBase
}
func (m *AddDashboardUIDMigration) SQL(dialect Dialect) string {
return "code migration"
}
func (m *AddDashboardUIDMigration) Exec(sess *xorm.Session, mg *Migrator) error {
return RunPreferencesMigration(sess, mg.Dialect.DriverName())
}
func RunPreferencesMigration(sess *xorm.Session, driverName string) error {
// sqlite
sql := `UPDATE preferences
SET home_dashboard_uid = (SELECT uid FROM dashboard WHERE dashboard.id = preferences.home_dashboard_id)
WHERE home_dashboard_uid IS NULL AND EXISTS (SELECT 1 FROM dashboard WHERE dashboard.id = preferences.home_dashboard_id);`
switch driverName {
case Postgres:
sql = `UPDATE preferences
SET home_dashboard_uid = dashboard.uid
FROM dashboard
WHERE preferences.home_dashboard_id = dashboard.id
AND (preferences.home_dashboard_uid IS NULL);`
case MySQL:
sql = `UPDATE preferences
LEFT JOIN dashboard ON preferences.home_dashboard_id = dashboard.id
SET preferences.home_dashboard_uid = dashboard.uid
WHERE preferences.home_dashboard_uid IS NULL;`
}
if _, err := sess.Exec(sql); err != nil {
return fmt.Errorf("failed to set home_dashboard_uid for preferences: %w", err)
}
return nil
}
+1 -1
View File
@@ -10148,7 +10148,7 @@
}
},
"put": {
"description": "Omitting a key (`theme`, `homeDashboardId`, `timezone`) will cause the current value to be replaced with the system default value.",
"description": "Omitting a key (`theme`, `homeDashboardUID`, `timezone`) will cause the current value to be replaced with the system default value.",
"tags": [
"user_preferences"
],
@@ -72,8 +72,6 @@ export type SuccessResponseBody = {
export type CookieType = string;
export type PatchPrefsCmd = {
cookies?: CookieType[];
/** The numerical :id of a favorited dashboard */
homeDashboardId?: number;
homeDashboardUID?: string;
language?: string;
locale?: string;
@@ -85,8 +83,6 @@ export type PatchPrefsCmd = {
};
export type UpdatePrefsCmd = {
cookies?: CookieType[];
/** The numerical :id of a favorited dashboard */
homeDashboardId?: number;
homeDashboardUID?: string;
language?: string;
locale?: string;
+1 -1
View File
@@ -24636,7 +24636,7 @@
]
},
"put": {
"description": "Omitting a key (`theme`, `homeDashboardId`, `timezone`) will cause the current value to be replaced with the system default value.",
"description": "Omitting a key (`theme`, `homeDashboardUID`, `timezone`) will cause the current value to be replaced with the system default value.",
"operationId": "updateUserPreferences",
"requestBody": {
"content": {