mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Introduce a preferences table to store Navbar preferences (#44914)
* First attempt at creating new navbar_preferences table in db * Apply to every nav item instead of just home * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * Chore: introduce initTestDB options for features * fix unit tests * Add another unit test and some logic for detecting if a preference already exists * tidy up * Only override IsFeatureToggleEnabled if it's defined * Extract setNavPreferences out into it's own function, initialise features correctly * Make the linter happy * Use new structure * user essentials mob! 🔱 * user essentials mob! 🔱 * Split NavbarPreferences from Preferences * user essentials mob! 🔱 * user essentials mob! 🔱 * Fix lint error * Start adding tests * Change internal db structure to be a generic json object * GetJsonData -> GetPreferencesJsonData * Stop using simplejson + add some more unit tests * Update pkg/api/preferences.go Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com> * Updates following review comments * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * Change patch to upsert, add a unit test * remove commented out code * introduce patch user/org preferences methods * Return Navbar preferences in the get call * Fix integration test by instantiating JsonData * Address review comments * Rename HideFromNavbar -> Hide * add swagger:model comment * Add patch to the preferences documentation * Add openapi annotations * Add a short description * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * user essentials mob! 🔱 * Update unit tests * remove unneeded url * remove outdated comment * Update integration tests * update generated swagger Co-authored-by: Alexandra Vargas <alexa1866@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com> Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
This commit is contained in:
parent
60af3af92c
commit
586272e5f0
@ -66,6 +66,34 @@ Content-Type: text/plain; charset=utf-8
|
|||||||
{"message":"Preferences updated"}
|
{"message":"Preferences updated"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Patch Current User Prefs
|
||||||
|
|
||||||
|
Update one or more preferences without modifying the others.
|
||||||
|
|
||||||
|
`PATCH /api/user/preferences`
|
||||||
|
|
||||||
|
**Example Request**:
|
||||||
|
|
||||||
|
```http
|
||||||
|
PATCH /api/user/preferences HTTP/1.1
|
||||||
|
Accept: application/json
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||||
|
|
||||||
|
{
|
||||||
|
"theme": "dark"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Response**:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 200
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
|
||||||
|
{"message":"Preferences updated"}
|
||||||
|
```
|
||||||
|
|
||||||
## Get Current Org Prefs
|
## Get Current Org Prefs
|
||||||
|
|
||||||
`GET /api/org/preferences`
|
`GET /api/org/preferences`
|
||||||
@ -115,3 +143,31 @@ Content-Type: text/plain; charset=utf-8
|
|||||||
|
|
||||||
{"message":"Preferences updated"}
|
{"message":"Preferences updated"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Patch Current Org Prefs
|
||||||
|
|
||||||
|
Update one or more preferences without modifying the others.
|
||||||
|
|
||||||
|
`PATCH /api/org/preferences`
|
||||||
|
|
||||||
|
**Example Request**:
|
||||||
|
|
||||||
|
```http
|
||||||
|
PATCH /api/org/preferences HTTP/1.1
|
||||||
|
Accept: application/json
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||||
|
|
||||||
|
{
|
||||||
|
"theme": "dark"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Response**:
|
||||||
|
|
||||||
|
```http
|
||||||
|
HTTP/1.1 200
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
|
||||||
|
{"message":"Preferences updated"}
|
||||||
|
```
|
||||||
|
@ -167,6 +167,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
|
|
||||||
userRoute.Get("/preferences", routing.Wrap(hs.GetUserPreferences))
|
userRoute.Get("/preferences", routing.Wrap(hs.GetUserPreferences))
|
||||||
userRoute.Put("/preferences", routing.Wrap(hs.UpdateUserPreferences))
|
userRoute.Put("/preferences", routing.Wrap(hs.UpdateUserPreferences))
|
||||||
|
userRoute.Patch("/preferences", routing.Wrap(hs.PatchUserPreferences))
|
||||||
|
|
||||||
userRoute.Get("/auth-tokens", routing.Wrap(hs.GetUserAuthTokens))
|
userRoute.Get("/auth-tokens", routing.Wrap(hs.GetUserAuthTokens))
|
||||||
userRoute.Post("/revoke-auth-token", routing.Wrap(hs.RevokeUserAuthToken))
|
userRoute.Post("/revoke-auth-token", routing.Wrap(hs.RevokeUserAuthToken))
|
||||||
@ -229,6 +230,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
// prefs
|
// prefs
|
||||||
orgRoute.Get("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesRead)), routing.Wrap(hs.GetOrgPreferences))
|
orgRoute.Get("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesRead)), routing.Wrap(hs.GetOrgPreferences))
|
||||||
orgRoute.Put("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.UpdateOrgPreferences))
|
orgRoute.Put("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.UpdateOrgPreferences))
|
||||||
|
orgRoute.Patch("/preferences", authorize(reqOrgAdmin, ac.EvalPermission(ActionOrgsPreferencesWrite)), routing.Wrap(hs.PatchOrgPreferences))
|
||||||
})
|
})
|
||||||
|
|
||||||
// current org without requirement of user to be org admin
|
// current org without requirement of user to be org admin
|
||||||
|
@ -20,3 +20,14 @@ package definitions
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 403: forbiddenError
|
// 403: forbiddenError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
|
|
||||||
|
// swagger:route PATCH /org/preferences org_preferences patchOrgPreferences
|
||||||
|
//
|
||||||
|
// Patch Current Org Prefs.
|
||||||
|
//
|
||||||
|
// Responses:
|
||||||
|
// 200: addOrgUser
|
||||||
|
// 400: badRequestError
|
||||||
|
// 401: unauthorisedError
|
||||||
|
// 403: forbiddenError
|
||||||
|
// 500: internalServerError
|
||||||
|
@ -23,6 +23,16 @@ import "github.com/grafana/grafana/pkg/api/dtos"
|
|||||||
// 401: unauthorisedError
|
// 401: unauthorisedError
|
||||||
// 500: internalServerError
|
// 500: internalServerError
|
||||||
|
|
||||||
|
// swagger:route PATCH /user/preferences user_preferences patchUserPreferences
|
||||||
|
//
|
||||||
|
// Patch user preferences.
|
||||||
|
//
|
||||||
|
// Responses:
|
||||||
|
// 200: okResponse
|
||||||
|
// 400: badRequestError
|
||||||
|
// 401: unauthorisedError
|
||||||
|
// 500: internalServerError
|
||||||
|
|
||||||
// swagger:parameters updateUserPreferences updateOrgPreferences updateTeamPreferences
|
// swagger:parameters updateUserPreferences updateOrgPreferences updateTeamPreferences
|
||||||
type UpdateUserPreferencesParam struct {
|
type UpdateUserPreferencesParam struct {
|
||||||
// in:body
|
// in:body
|
||||||
@ -35,3 +45,10 @@ type GetPreferencesResponse struct {
|
|||||||
// in:body
|
// in:body
|
||||||
Body dtos.Prefs `json:"body"`
|
Body dtos.Prefs `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swagger:parameters patchUserPreferences patchOrgPreferences patchTeamPreferences
|
||||||
|
type PatchUserPreferencesParam struct {
|
||||||
|
// in:body
|
||||||
|
// required:true
|
||||||
|
Body dtos.PatchPrefsCmd `json:"body"`
|
||||||
|
}
|
||||||
|
@ -37,7 +37,8 @@ const (
|
|||||||
// are negative to ensure that the default items are placed above
|
// are negative to ensure that the default items are placed above
|
||||||
// any items with default weight.
|
// any items with default weight.
|
||||||
|
|
||||||
WeightHome = (iota - 20) * 100
|
WeightSavedItems = (iota - 20) * 100
|
||||||
|
WeightHome
|
||||||
WeightCreate
|
WeightCreate
|
||||||
WeightDashboard
|
WeightDashboard
|
||||||
WeightExplore
|
WeightExplore
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package dtos
|
package dtos
|
||||||
|
|
||||||
|
import "github.com/grafana/grafana/pkg/models"
|
||||||
|
|
||||||
type Prefs struct {
|
type Prefs struct {
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme"`
|
||||||
HomeDashboardID int64 `json:"homeDashboardId"`
|
HomeDashboardID int64 `json:"homeDashboardId"`
|
||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone"`
|
||||||
WeekStart string `json:"weekStart"`
|
WeekStart string `json:"weekStart"`
|
||||||
|
Navbar models.NavbarPreference `json:"navbar,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:model
|
// swagger:model
|
||||||
@ -15,6 +18,20 @@ type UpdatePrefsCmd struct {
|
|||||||
// Default:0
|
// Default:0
|
||||||
HomeDashboardID int64 `json:"homeDashboardId"`
|
HomeDashboardID int64 `json:"homeDashboardId"`
|
||||||
// Enum: utc,browser
|
// Enum: utc,browser
|
||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone"`
|
||||||
WeekStart string `json:"weekStart"`
|
WeekStart string `json:"weekStart"`
|
||||||
|
Navbar *models.NavbarPreference `json:"navbar,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// swagger:model
|
||||||
|
type PatchPrefsCmd struct {
|
||||||
|
// Enum: light,dark
|
||||||
|
Theme *string `json:"theme,omitempty"`
|
||||||
|
// The numerical :id of a favorited dashboard
|
||||||
|
// Default:0
|
||||||
|
HomeDashboardID *int64 `json:"homeDashboardId,omitempty"`
|
||||||
|
// Enum: utc,browser
|
||||||
|
Timezone *string `json:"timezone,omitempty"`
|
||||||
|
WeekStart *string `json:"weekStart,omitempty"`
|
||||||
|
Navbar *models.NavbarPreference `json:"navbar,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,20 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
|
|||||||
navTree := []*dtos.NavLink{}
|
navTree := []*dtos.NavLink{}
|
||||||
|
|
||||||
if hs.Features.IsEnabled(featuremgmt.FlagNewNavigation) {
|
if hs.Features.IsEnabled(featuremgmt.FlagNewNavigation) {
|
||||||
|
savedItemsLinks, err := hs.buildSavedItemsNavLinks(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
navTree = append(navTree, &dtos.NavLink{
|
||||||
|
Text: "Saved Items",
|
||||||
|
Id: "saved-items",
|
||||||
|
Icon: "heart",
|
||||||
|
SortWeight: dtos.WeightSavedItems,
|
||||||
|
Section: dtos.NavSectionCore,
|
||||||
|
Children: savedItemsLinks,
|
||||||
|
})
|
||||||
|
|
||||||
navTree = append(navTree, &dtos.NavLink{
|
navTree = append(navTree, &dtos.NavLink{
|
||||||
Text: "Home",
|
Text: "Home",
|
||||||
Id: "home",
|
Id: "home",
|
||||||
@ -388,6 +402,30 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
|
|||||||
return navTree, nil
|
return navTree, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) buildSavedItemsNavLinks(c *models.ReqContext) ([]*dtos.NavLink, error) {
|
||||||
|
savedItemsChildNavs := []*dtos.NavLink{}
|
||||||
|
|
||||||
|
// query preferences table for any saved items
|
||||||
|
prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser}
|
||||||
|
if err := hs.SQLStore.GetPreferencesWithDefaults(c.Req.Context(), &prefsQuery); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
savedItems := prefsQuery.Result.JsonData.Navbar.SavedItems
|
||||||
|
|
||||||
|
if len(savedItems) > 0 {
|
||||||
|
for _, savedItem := range savedItems {
|
||||||
|
savedItemsChildNavs = append(savedItemsChildNavs, &dtos.NavLink{
|
||||||
|
Id: savedItem.Id,
|
||||||
|
Text: savedItem.Text,
|
||||||
|
Url: savedItem.Url,
|
||||||
|
Target: savedItem.Target,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedItemsChildNavs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm bool) []*dtos.NavLink {
|
func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm bool) []*dtos.NavLink {
|
||||||
dashboardChildNavs := []*dtos.NavLink{}
|
dashboardChildNavs := []*dtos.NavLink{}
|
||||||
if !hs.Features.IsEnabled(featuremgmt.FlagNewNavigation) {
|
if !hs.Features.IsEnabled(featuremgmt.FlagNewNavigation) {
|
||||||
|
@ -51,6 +51,10 @@ func (hs *HTTPServer) getPreferencesFor(ctx context.Context, orgID, userID, team
|
|||||||
WeekStart: prefsQuery.Result.WeekStart,
|
WeekStart: prefsQuery.Result.WeekStart,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prefsQuery.Result.JsonData != nil {
|
||||||
|
dto.Navbar = prefsQuery.Result.JsonData.Navbar
|
||||||
|
}
|
||||||
|
|
||||||
return response.JSON(200, &dto)
|
return response.JSON(200, &dto)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +88,37 @@ func (hs *HTTPServer) updatePreferencesFor(ctx context.Context, orgID, userID, t
|
|||||||
return response.Success("Preferences updated")
|
return response.Success("Preferences updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PATCH /api/user/preferences
|
||||||
|
func (hs *HTTPServer) PatchUserPreferences(c *models.ReqContext) response.Response {
|
||||||
|
dtoCmd := dtos.PatchPrefsCmd{}
|
||||||
|
if err := web.Bind(c.Req, &dtoCmd); err != nil {
|
||||||
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||||
|
}
|
||||||
|
return hs.patchPreferencesFor(c.Req.Context(), c.OrgId, c.UserId, 0, &dtoCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, teamId int64, dtoCmd *dtos.PatchPrefsCmd) response.Response {
|
||||||
|
if dtoCmd.Theme != nil && *dtoCmd.Theme != lightTheme && *dtoCmd.Theme != darkTheme && *dtoCmd.Theme != defaultTheme {
|
||||||
|
return response.Error(400, "Invalid theme", nil)
|
||||||
|
}
|
||||||
|
patchCmd := models.PatchPreferencesCommand{
|
||||||
|
UserId: userID,
|
||||||
|
OrgId: orgID,
|
||||||
|
TeamId: teamId,
|
||||||
|
Theme: dtoCmd.Theme,
|
||||||
|
Timezone: dtoCmd.Timezone,
|
||||||
|
WeekStart: dtoCmd.WeekStart,
|
||||||
|
HomeDashboardId: dtoCmd.HomeDashboardID,
|
||||||
|
Navbar: dtoCmd.Navbar,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hs.SQLStore.PatchPreferences(ctx, &patchCmd); err != nil {
|
||||||
|
return response.Error(500, "Failed to save preferences", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success("Preferences updated")
|
||||||
|
}
|
||||||
|
|
||||||
// GET /api/org/preferences
|
// GET /api/org/preferences
|
||||||
func (hs *HTTPServer) GetOrgPreferences(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetOrgPreferences(c *models.ReqContext) response.Response {
|
||||||
return hs.getPreferencesFor(c.Req.Context(), c.OrgId, 0, 0)
|
return hs.getPreferencesFor(c.Req.Context(), c.OrgId, 0, 0)
|
||||||
@ -97,3 +132,12 @@ func (hs *HTTPServer) UpdateOrgPreferences(c *models.ReqContext) response.Respon
|
|||||||
}
|
}
|
||||||
return hs.updatePreferencesFor(c.Req.Context(), c.OrgId, 0, 0, &dtoCmd)
|
return hs.updatePreferencesFor(c.Req.Context(), c.OrgId, 0, 0, &dtoCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PATCH /api/org/preferences
|
||||||
|
func (hs *HTTPServer) PatchOrgPreferences(c *models.ReqContext) response.Response {
|
||||||
|
dtoCmd := dtos.PatchPrefsCmd{}
|
||||||
|
if err := web.Bind(c.Req, &dtoCmd); err != nil {
|
||||||
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||||
|
}
|
||||||
|
return hs.patchPreferencesFor(c.Req.Context(), c.OrgId, 0, 0, &dtoCmd)
|
||||||
|
}
|
||||||
|
@ -11,10 +11,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
getOrgPreferencesURL = "/api/org/preferences/"
|
getOrgPreferencesURL = "/api/org/preferences/"
|
||||||
putOrgPreferencesURL = "/api/org/preferences/"
|
putOrgPreferencesURL = "/api/org/preferences/"
|
||||||
|
patchOrgPreferencesUrl = "/api/org/preferences/"
|
||||||
|
patchUserPreferencesUrl = "/api/user/preferences/"
|
||||||
|
|
||||||
testUpdateOrgPreferencesCmd = `{ "theme": "light", "homeDashboardId": 1 }`
|
testUpdateOrgPreferencesCmd = `{ "theme": "light", "homeDashboardId": 1 }`
|
||||||
|
testPatchOrgPreferencesCmd = `{"navbar":{"savedItems":[{"id":"snapshots","text":"Snapshots","icon":"camera","url":"/dashboard/snapshots"}]}}`
|
||||||
|
testPatchOrgPreferencesCmdBad = `this is not json`
|
||||||
|
testPatchUserPreferencesCmd = `{"navbar":{"savedItems":[{"id":"snapshots","text":"Snapshots","icon":"camera","url":"/dashboard/snapshots"}]}}`
|
||||||
|
testPatchUserPreferencesCmdBad = `this is not json`
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPIEndpoint_GetCurrentOrgPreferences_LegacyAccessControl(t *testing.T) {
|
func TestAPIEndpoint_GetCurrentOrgPreferences_LegacyAccessControl(t *testing.T) {
|
||||||
@ -109,3 +115,43 @@ func TestAPIEndpoint_PutCurrentOrgPreferences_AccessControl(t *testing.T) {
|
|||||||
assert.Equal(t, http.StatusForbidden, response.Code)
|
assert.Equal(t, http.StatusForbidden, response.Code)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIEndpoint_PatchUserPreferences(t *testing.T) {
|
||||||
|
sc := setupHTTPServer(t, true, false)
|
||||||
|
|
||||||
|
_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
setInitCtxSignedInOrgAdmin(sc.initCtx)
|
||||||
|
input := strings.NewReader(testPatchUserPreferencesCmd)
|
||||||
|
t.Run("Returns 200 on success", func(t *testing.T) {
|
||||||
|
response := callAPI(sc.server, http.MethodPatch, patchUserPreferencesUrl, input, t)
|
||||||
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
})
|
||||||
|
|
||||||
|
input = strings.NewReader(testPatchUserPreferencesCmdBad)
|
||||||
|
t.Run("Returns 400 with bad data", func(t *testing.T) {
|
||||||
|
response := callAPI(sc.server, http.MethodPut, patchUserPreferencesUrl, input, t)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, response.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIEndpoint_PatchOrgPreferences(t *testing.T) {
|
||||||
|
sc := setupHTTPServer(t, true, false)
|
||||||
|
|
||||||
|
_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
setInitCtxSignedInOrgAdmin(sc.initCtx)
|
||||||
|
input := strings.NewReader(testPatchOrgPreferencesCmd)
|
||||||
|
t.Run("Returns 200 on success", func(t *testing.T) {
|
||||||
|
response := callAPI(sc.server, http.MethodPatch, patchOrgPreferencesUrl, input, t)
|
||||||
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
})
|
||||||
|
|
||||||
|
input = strings.NewReader(testPatchOrgPreferencesCmdBad)
|
||||||
|
t.Run("Returns 400 with bad data", func(t *testing.T) {
|
||||||
|
response := callAPI(sc.server, http.MethodPut, patchOrgPreferencesUrl, input, t)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, response.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type NavLink struct {
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
Target string `json:"target,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Preferences struct {
|
type Preferences struct {
|
||||||
Id int64
|
Id int64
|
||||||
OrgId int64
|
OrgId int64
|
||||||
@ -16,6 +25,32 @@ type Preferences struct {
|
|||||||
Theme string
|
Theme string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
|
JsonData *PreferencesJsonData
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following needed for to implement the xorm/database ORM Conversion interface do the
|
||||||
|
// conversion when reading/writing to the database, see https://gobook.io/read/gitea.com/xorm/manual-en-US/chapter-02/4.columns.html.
|
||||||
|
|
||||||
|
func (j *PreferencesJsonData) FromDB(data []byte) error {
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(data))
|
||||||
|
dec.UseNumber()
|
||||||
|
return dec.Decode(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *PreferencesJsonData) ToDB() ([]byte, error) {
|
||||||
|
if j == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NavbarPreference struct {
|
||||||
|
SavedItems []NavLink `json:"savedItems"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreferencesJsonData struct {
|
||||||
|
Navbar NavbarPreference `json:"navbar"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
@ -43,8 +78,21 @@ type SavePreferencesCommand struct {
|
|||||||
OrgId int64
|
OrgId int64
|
||||||
TeamId int64
|
TeamId int64
|
||||||
|
|
||||||
HomeDashboardId int64 `json:"homeDashboardId"`
|
HomeDashboardId int64 `json:"homeDashboardId,omitempty"`
|
||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone,omitempty"`
|
||||||
WeekStart string `json:"weekStart"`
|
WeekStart string `json:"weekStart,omitempty"`
|
||||||
Theme string `json:"theme"`
|
Theme string `json:"theme,omitempty"`
|
||||||
|
Navbar *NavbarPreference `json:"navbar,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PatchPreferencesCommand 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"`
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
import (
|
||||||
|
. "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
|
)
|
||||||
|
|
||||||
func addPreferencesMigrations(mg *Migrator) {
|
func addPreferencesMigrations(mg *Migrator) {
|
||||||
mg.AddMigration("drop preferences table v2", NewDropTableMigration("preferences"))
|
mg.AddMigration("drop preferences table v2", NewDropTableMigration("preferences"))
|
||||||
@ -46,4 +48,11 @@ func addPreferencesMigrations(mg *Migrator) {
|
|||||||
mg.AddMigration("Add column week_start in preferences", NewAddColumnMigration(preferencesV2, &Column{
|
mg.AddMigration("Add column week_start in preferences", NewAddColumnMigration(preferencesV2, &Column{
|
||||||
Name: "week_start", Type: DB_NVarchar, Length: 10, Nullable: true,
|
Name: "week_start", Type: DB_NVarchar, Length: 10, Nullable: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
mg.AddMigration("Add column preferences.json_data", NewAddColumnMigration(preferencesV2, &Column{
|
||||||
|
Name: "json_data", Type: DB_Text, Nullable: true,
|
||||||
|
}))
|
||||||
|
// change column type of preferences.json_data
|
||||||
|
mg.AddMigration("alter preferences.json_data to mediumtext v1", NewRawSQLMigration("").
|
||||||
|
Mysql("ALTER TABLE preferences MODIFY json_data MEDIUMTEXT;"))
|
||||||
}
|
}
|
||||||
|
@ -287,6 +287,10 @@ func (m *SQLStoreMock) SavePreferences(ctx context.Context, cmd *models.SavePref
|
|||||||
return m.ExpectedError
|
return m.ExpectedError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *SQLStoreMock) PatchPreferences(ctx context.Context, cmd *models.PatchPreferencesCommand) error {
|
||||||
|
return m.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
func (m *SQLStoreMock) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
func (m *SQLStoreMock) GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error) {
|
||||||
return nil, m.ExpectedError
|
return nil, m.ExpectedError
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ func (ss *SQLStore) addPreferencesQueryAndCommandHandlers() {
|
|||||||
bus.AddHandler("sql", ss.GetPreferences)
|
bus.AddHandler("sql", ss.GetPreferences)
|
||||||
bus.AddHandler("sql", ss.GetPreferencesWithDefaults)
|
bus.AddHandler("sql", ss.GetPreferencesWithDefaults)
|
||||||
bus.AddHandler("sql", ss.SavePreferences)
|
bus.AddHandler("sql", ss.SavePreferences)
|
||||||
|
bus.AddHandler("sql", ss.PatchPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *SQLStore) GetPreferencesWithDefaults(ctx context.Context, query *models.GetPreferencesWithDefaultsQuery) error {
|
func (ss *SQLStore) GetPreferencesWithDefaults(ctx context.Context, query *models.GetPreferencesWithDefaultsQuery) error {
|
||||||
@ -46,6 +47,7 @@ func (ss *SQLStore) GetPreferencesWithDefaults(ctx context.Context, query *model
|
|||||||
Timezone: ss.Cfg.DateFormats.DefaultTimezone,
|
Timezone: ss.Cfg.DateFormats.DefaultTimezone,
|
||||||
WeekStart: ss.Cfg.DateFormats.DefaultWeekStart,
|
WeekStart: ss.Cfg.DateFormats.DefaultWeekStart,
|
||||||
HomeDashboardId: 0,
|
HomeDashboardId: 0,
|
||||||
|
JsonData: &models.PreferencesJsonData{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range prefs {
|
for _, p := range prefs {
|
||||||
@ -61,6 +63,9 @@ func (ss *SQLStore) GetPreferencesWithDefaults(ctx context.Context, query *model
|
|||||||
if p.HomeDashboardId != 0 {
|
if p.HomeDashboardId != 0 {
|
||||||
res.HomeDashboardId = p.HomeDashboardId
|
res.HomeDashboardId = p.HomeDashboardId
|
||||||
}
|
}
|
||||||
|
if p.JsonData != nil {
|
||||||
|
res.JsonData = p.JsonData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query.Result = res
|
query.Result = res
|
||||||
@ -106,10 +111,24 @@ func (ss *SQLStore) SavePreferences(ctx context.Context, cmd *models.SavePrefere
|
|||||||
Theme: cmd.Theme,
|
Theme: cmd.Theme,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
|
JsonData: &models.PreferencesJsonData{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Navbar != nil {
|
||||||
|
prefs.JsonData.Navbar = *cmd.Navbar
|
||||||
}
|
}
|
||||||
_, err = sess.Insert(&prefs)
|
_, err = sess.Insert(&prefs)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Wrap this in an if statement to maintain backwards compatibility
|
||||||
|
if cmd.Navbar != nil {
|
||||||
|
if prefs.JsonData == nil {
|
||||||
|
prefs.JsonData = &models.PreferencesJsonData{}
|
||||||
|
}
|
||||||
|
if cmd.Navbar.SavedItems != nil {
|
||||||
|
prefs.JsonData.Navbar.SavedItems = cmd.Navbar.SavedItems
|
||||||
|
}
|
||||||
|
}
|
||||||
prefs.HomeDashboardId = cmd.HomeDashboardId
|
prefs.HomeDashboardId = cmd.HomeDashboardId
|
||||||
prefs.Timezone = cmd.Timezone
|
prefs.Timezone = cmd.Timezone
|
||||||
prefs.WeekStart = cmd.WeekStart
|
prefs.WeekStart = cmd.WeekStart
|
||||||
@ -120,3 +139,58 @@ func (ss *SQLStore) SavePreferences(ctx context.Context, cmd *models.SavePrefere
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ss *SQLStore) PatchPreferences(ctx context.Context, cmd *models.PatchPreferencesCommand) error {
|
||||||
|
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||||
|
var prefs models.Preferences
|
||||||
|
exists, err := sess.Where("org_id=? AND user_id=? AND team_id=?", cmd.OrgId, cmd.UserId, cmd.TeamId).Get(&prefs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
prefs = models.Preferences{
|
||||||
|
UserId: cmd.UserId,
|
||||||
|
OrgId: cmd.OrgId,
|
||||||
|
TeamId: cmd.TeamId,
|
||||||
|
Created: time.Now(),
|
||||||
|
JsonData: &models.PreferencesJsonData{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Navbar != nil {
|
||||||
|
if prefs.JsonData == nil {
|
||||||
|
prefs.JsonData = &models.PreferencesJsonData{}
|
||||||
|
}
|
||||||
|
if cmd.Navbar.SavedItems != nil {
|
||||||
|
prefs.JsonData.Navbar.SavedItems = cmd.Navbar.SavedItems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.HomeDashboardId != nil {
|
||||||
|
prefs.HomeDashboardId = *cmd.HomeDashboardId
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Timezone != nil {
|
||||||
|
prefs.Timezone = *cmd.Timezone
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.WeekStart != nil {
|
||||||
|
prefs.WeekStart = *cmd.WeekStart
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Theme != nil {
|
||||||
|
prefs.Theme = *cmd.Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
prefs.Updated = time.Now()
|
||||||
|
prefs.Version += 1
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
_, err = sess.ID(prefs.Id).AllCols().Update(&prefs)
|
||||||
|
} else {
|
||||||
|
_, err = sess.Insert(&prefs)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -15,6 +15,48 @@ import (
|
|||||||
|
|
||||||
func TestPreferencesDataAccess(t *testing.T) {
|
func TestPreferencesDataAccess(t *testing.T) {
|
||||||
ss := InitTestDB(t)
|
ss := InitTestDB(t)
|
||||||
|
emptyNavbarPreferences := models.NavbarPreference{}
|
||||||
|
userNavbarPreferences := models.NavbarPreference{
|
||||||
|
SavedItems: []models.NavLink{{
|
||||||
|
Id: "explore",
|
||||||
|
Text: "Explore",
|
||||||
|
Url: "/explore",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
orgNavbarPreferences := models.NavbarPreference{
|
||||||
|
SavedItems: []models.NavLink{{
|
||||||
|
Id: "alerting",
|
||||||
|
Text: "Alerting",
|
||||||
|
Url: "/alerting",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
team1NavbarPreferences := models.NavbarPreference{
|
||||||
|
SavedItems: []models.NavLink{{
|
||||||
|
Id: "dashboards",
|
||||||
|
Text: "Dashboards",
|
||||||
|
Url: "/dashboards",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
team2NavbarPreferences := models.NavbarPreference{
|
||||||
|
SavedItems: []models.NavLink{{
|
||||||
|
Id: "home",
|
||||||
|
Text: "Home",
|
||||||
|
Url: "/home",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
emptyPreferencesJsonData := models.PreferencesJsonData{
|
||||||
|
Navbar: emptyNavbarPreferences,
|
||||||
|
}
|
||||||
|
userPreferencesJsonData := models.PreferencesJsonData{
|
||||||
|
Navbar: userNavbarPreferences,
|
||||||
|
}
|
||||||
|
orgPreferencesJsonData := models.PreferencesJsonData{
|
||||||
|
Navbar: orgNavbarPreferences,
|
||||||
|
}
|
||||||
|
team2PreferencesJsonData := models.PreferencesJsonData{
|
||||||
|
Navbar: team2NavbarPreferences,
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("GetPreferencesWithDefaults with no saved preferences should return defaults", func(t *testing.T) {
|
t.Run("GetPreferencesWithDefaults with no saved preferences should return defaults", func(t *testing.T) {
|
||||||
ss.Cfg.DefaultTheme = "light"
|
ss.Cfg.DefaultTheme = "light"
|
||||||
@ -26,6 +68,7 @@ func TestPreferencesDataAccess(t *testing.T) {
|
|||||||
require.Equal(t, "light", query.Result.Theme)
|
require.Equal(t, "light", query.Result.Theme)
|
||||||
require.Equal(t, "UTC", query.Result.Timezone)
|
require.Equal(t, "UTC", query.Result.Timezone)
|
||||||
require.Equal(t, int64(0), query.Result.HomeDashboardId)
|
require.Equal(t, int64(0), query.Result.HomeDashboardId)
|
||||||
|
require.Equal(t, &emptyPreferencesJsonData, query.Result.JsonData)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetPreferencesWithDefaults with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
|
t.Run("GetPreferencesWithDefaults with saved org and user home dashboard should return user home dashboard", func(t *testing.T) {
|
||||||
@ -118,8 +161,98 @@ func TestPreferencesDataAccess(t *testing.T) {
|
|||||||
require.Equal(t, int64(1), query.Result.HomeDashboardId)
|
require.Equal(t, int64(1), query.Result.HomeDashboardId)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org and user json data should return user json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, UserId: 1, Navbar: &userNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 1}}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &userPreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org and other user json data should return org json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, UserId: 1, Navbar: &userNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 2}}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &orgPreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org and teams json data should return last team json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 2, Navbar: &team1NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 3, Navbar: &team2NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{
|
||||||
|
User: &models.SignedInUser{OrgId: 1, Teams: []int64{2, 3}},
|
||||||
|
}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &team2PreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org and other teams json data should return org json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 2, Navbar: &team1NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 3, Navbar: &team2NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1}}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &orgPreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org, teams and user json data should return user json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 2, Navbar: &team1NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 3, Navbar: &team2NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, UserId: 1, Navbar: &userNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{
|
||||||
|
User: &models.SignedInUser{OrgId: 1, UserId: 1, Teams: []int64{2, 3}},
|
||||||
|
}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &userPreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("GetPreferencesWithDefaults with saved org, other teams and user json data should return org json data", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 2, Navbar: &team1NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, TeamId: 3, Navbar: &team2NavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{OrgId: 1, UserId: 1, Navbar: &userNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{
|
||||||
|
User: &models.SignedInUser{OrgId: 1, UserId: 2},
|
||||||
|
}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, &orgPreferencesJsonData, query.Result.JsonData)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("SavePreferences for a user should store correct values", func(t *testing.T) {
|
t.Run("SavePreferences for a user should store correct values", func(t *testing.T) {
|
||||||
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{UserId: models.SignedInUser{}.UserId, Theme: "dark", Timezone: "browser", HomeDashboardId: 5, WeekStart: "1"})
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{UserId: models.SignedInUser{}.UserId, Theme: "dark", Timezone: "browser", HomeDashboardId: 5, WeekStart: "1", Navbar: &userNavbarPreferences})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{}}
|
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{}}
|
||||||
@ -132,6 +265,33 @@ func TestPreferencesDataAccess(t *testing.T) {
|
|||||||
Timezone: "browser",
|
Timezone: "browser",
|
||||||
WeekStart: "1",
|
WeekStart: "1",
|
||||||
Theme: "dark",
|
Theme: "dark",
|
||||||
|
JsonData: &userPreferencesJsonData,
|
||||||
|
Created: query.Result.Created,
|
||||||
|
Updated: query.Result.Updated,
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(expected, query.Result); diff != "" {
|
||||||
|
t.Fatalf("Result mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PatchPreferences for a user should only modify a single value", func(t *testing.T) {
|
||||||
|
err := ss.SavePreferences(context.Background(), &models.SavePreferencesCommand{UserId: models.SignedInUser{}.UserId, Theme: "dark", Timezone: "browser", HomeDashboardId: 5, WeekStart: "1", Navbar: &orgNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = ss.PatchPreferences(context.Background(), &models.PatchPreferencesCommand{UserId: models.SignedInUser{}.UserId, Navbar: &userNavbarPreferences})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{}}
|
||||||
|
err = ss.GetPreferencesWithDefaults(context.Background(), query)
|
||||||
|
require.NoError(t, err)
|
||||||
|
expected := &models.Preferences{
|
||||||
|
Id: query.Result.Id,
|
||||||
|
Version: query.Result.Version,
|
||||||
|
HomeDashboardId: 5,
|
||||||
|
Timezone: "browser",
|
||||||
|
WeekStart: "1",
|
||||||
|
Theme: "dark",
|
||||||
|
JsonData: &userPreferencesJsonData,
|
||||||
Created: query.Result.Created,
|
Created: query.Result.Created,
|
||||||
Updated: query.Result.Updated,
|
Updated: query.Result.Updated,
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ type Store interface {
|
|||||||
GetPreferencesWithDefaults(ctx context.Context, query *models.GetPreferencesWithDefaultsQuery) error
|
GetPreferencesWithDefaults(ctx context.Context, query *models.GetPreferencesWithDefaultsQuery) error
|
||||||
GetPreferences(ctx context.Context, query *models.GetPreferencesQuery) error
|
GetPreferences(ctx context.Context, query *models.GetPreferencesQuery) error
|
||||||
SavePreferences(ctx context.Context, cmd *models.SavePreferencesCommand) error
|
SavePreferences(ctx context.Context, cmd *models.SavePreferencesCommand) error
|
||||||
|
PatchPreferences(ctx context.Context, cmd *models.PatchPreferencesCommand) error
|
||||||
GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error)
|
GetPluginSettings(ctx context.Context, orgID int64) ([]*models.PluginSettingInfoDTO, error)
|
||||||
GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error
|
GetPluginSettingById(ctx context.Context, query *models.GetPluginSettingByIdQuery) error
|
||||||
UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error
|
UpdatePluginSetting(ctx context.Context, cmd *models.UpdatePluginSettingCmd) error
|
||||||
|
File diff suppressed because it is too large
Load Diff
2965
public/api-spec.json
2965
public/api-spec.json
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user