diff --git a/pkg/api/api.go b/pkg/api/api.go index f1fe940e416..7714834d0b9 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -155,6 +155,8 @@ func (hs *HTTPServer) registerRoutes() { teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) teamsRoute.Delete("/:teamId/members/:userId", Wrap(RemoveTeamMember)) + teamsRoute.Get("/:teamId/preferences", Wrap(GetTeamPreferences)) + teamsRoute.Put("/:teamId/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateTeamPreferences)) }, reqOrgAdmin) // team without requirement of user to be org admin diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 02248334b9c..6abb72f1559 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -293,7 +293,7 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response { } func GetHomeDashboard(c *m.ReqContext) Response { - prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId} + prefsQuery := m.GetPreferencesWithDefaultsQuery{User: c.SignedInUser} if err := bus.Dispatch(&prefsQuery); err != nil { return Error(500, "Failed to get preferences", err) } diff --git a/pkg/api/index.go b/pkg/api/index.go index fe7c9e79a17..253fa9c17af 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -23,7 +23,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er return nil, err } - prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId} + prefsQuery := m.GetPreferencesWithDefaultsQuery{User: c.SignedInUser} if err := bus.Dispatch(&prefsQuery); err != nil { return nil, err } diff --git a/pkg/api/preferences.go b/pkg/api/preferences.go index 26695130975..9b451aa2a6e 100644 --- a/pkg/api/preferences.go +++ b/pkg/api/preferences.go @@ -21,11 +21,11 @@ func SetHomeDashboard(c *m.ReqContext, cmd m.SavePreferencesCommand) Response { // GET /api/user/preferences func GetUserPreferences(c *m.ReqContext) Response { - return getPreferencesFor(c.OrgId, c.UserId) + return getPreferencesFor(c.OrgId, c.UserId, 0) } -func getPreferencesFor(orgID int64, userID int64) Response { - prefsQuery := m.GetPreferencesQuery{UserId: userID, OrgId: orgID} +func getPreferencesFor(orgID, userID, teamID int64) Response { + prefsQuery := m.GetPreferencesQuery{UserId: userID, OrgId: orgID, TeamId: teamID} if err := bus.Dispatch(&prefsQuery); err != nil { return Error(500, "Failed to get preferences", err) @@ -42,13 +42,14 @@ func getPreferencesFor(orgID int64, userID int64) Response { // PUT /api/user/preferences func UpdateUserPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { - return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd) + return updatePreferencesFor(c.OrgId, c.UserId, 0, &dtoCmd) } -func updatePreferencesFor(orgID int64, userID int64, dtoCmd *dtos.UpdatePrefsCmd) Response { +func updatePreferencesFor(orgID, userID, teamId int64, dtoCmd *dtos.UpdatePrefsCmd) Response { saveCmd := m.SavePreferencesCommand{ UserId: userID, OrgId: orgID, + TeamId: teamId, Theme: dtoCmd.Theme, Timezone: dtoCmd.Timezone, HomeDashboardId: dtoCmd.HomeDashboardID, @@ -63,10 +64,10 @@ func updatePreferencesFor(orgID int64, userID int64, dtoCmd *dtos.UpdatePrefsCmd // GET /api/org/preferences func GetOrgPreferences(c *m.ReqContext) Response { - return getPreferencesFor(c.OrgId, 0) + return getPreferencesFor(c.OrgId, 0, 0) } // PUT /api/org/preferences func UpdateOrgPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { - return updatePreferencesFor(c.OrgId, 0, &dtoCmd) + return updatePreferencesFor(c.OrgId, 0, 0, &dtoCmd) } diff --git a/pkg/api/team.go b/pkg/api/team.go index ebb426c4c82..32265e5d018 100644 --- a/pkg/api/team.go +++ b/pkg/api/team.go @@ -96,3 +96,13 @@ func GetTeamByID(c *m.ReqContext) Response { query.Result.AvatarUrl = dtos.GetGravatarUrlWithDefault(query.Result.Email, query.Result.Name) return JSON(200, &query.Result) } + +// GET /api/teams/:teamId/preferences +func GetTeamPreferences(c *m.ReqContext) Response { + return getPreferencesFor(c.OrgId, 0, c.ParamsInt64(":teamId")) +} + +// PUT /api/teams/:teamId/preferences +func UpdateTeamPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response { + return updatePreferencesFor(c.OrgId, 0, c.ParamsInt64(":teamId"), &dtoCmd) +} diff --git a/pkg/models/preferences.go b/pkg/models/preferences.go index 4c77bc96d4d..c73e0be4949 100644 --- a/pkg/models/preferences.go +++ b/pkg/models/preferences.go @@ -14,6 +14,7 @@ type Preferences struct { Id int64 OrgId int64 UserId int64 + TeamId int64 Version int HomeDashboardId int64 Timezone string @@ -29,14 +30,13 @@ type GetPreferencesQuery struct { Id int64 OrgId int64 UserId int64 + TeamId int64 Result *Preferences } type GetPreferencesWithDefaultsQuery struct { - Id int64 - OrgId int64 - UserId int64 + User *SignedInUser Result *Preferences } @@ -46,6 +46,7 @@ type GetPreferencesWithDefaultsQuery struct { type SavePreferencesCommand struct { UserId int64 OrgId int64 + TeamId int64 HomeDashboardId int64 `json:"homeDashboardId"` Timezone string `json:"timezone"` diff --git a/pkg/services/sqlstore/migrations/preferences_mig.go b/pkg/services/sqlstore/migrations/preferences_mig.go index b3822fe5239..d8134f11f79 100644 --- a/pkg/services/sqlstore/migrations/preferences_mig.go +++ b/pkg/services/sqlstore/migrations/preferences_mig.go @@ -34,4 +34,13 @@ func addPreferencesMigrations(mg *Migrator) { {Name: "timezone", Type: DB_NVarchar, Length: 50, Nullable: false}, {Name: "theme", Type: DB_NVarchar, Length: 20, Nullable: false}, })) + + mg.AddMigration("Add column team_id in preferences", NewAddColumnMigration(preferencesV2, &Column{ + Name: "team_id", Type: DB_BigInt, Nullable: true, + })) + + mg.AddMigration("Update team_id column values in preferences", NewRawSqlMigration(""). + Sqlite("UPDATE preferences SET team_id=0 WHERE team_id IS NULL;"). + Postgres("UPDATE preferences SET team_id=0 WHERE team_id IS NULL;"). + Mysql("UPDATE preferences SET team_id=0 WHERE team_id IS NULL;")) } diff --git a/pkg/services/sqlstore/preferences.go b/pkg/services/sqlstore/preferences.go index 04e787971d9..858a2c77075 100644 --- a/pkg/services/sqlstore/preferences.go +++ b/pkg/services/sqlstore/preferences.go @@ -1,6 +1,7 @@ package sqlstore import ( + "strings" "time" "github.com/grafana/grafana/pkg/bus" @@ -16,11 +17,22 @@ func init() { } func GetPreferencesWithDefaults(query *m.GetPreferencesWithDefaultsQuery) error { - + params := make([]interface{}, 0) + filter := "" + if len(query.User.Teams) > 0 { + filter = "(org_id=? AND team_id IN (?" + strings.Repeat(",?", len(query.User.Teams)-1) + ")) OR " + params = append(params, query.User.OrgId) + for _, v := range query.User.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.User.OrgId) + params = append(params, query.User.UserId) + params = append(params, query.User.OrgId) prefs := make([]*m.Preferences, 0) - filter := "(org_id=? AND user_id=?) OR (org_id=? AND user_id=0)" - err := x.Where(filter, query.OrgId, query.UserId, query.OrgId). - OrderBy("user_id ASC"). + err := x.Where(filter, params...). + OrderBy("user_id ASC, team_id ASC"). Find(&prefs) if err != nil { @@ -50,9 +62,8 @@ func GetPreferencesWithDefaults(query *m.GetPreferencesWithDefaultsQuery) error } func GetPreferences(query *m.GetPreferencesQuery) error { - var prefs m.Preferences - exists, err := x.Where("org_id=? AND user_id=?", query.OrgId, query.UserId).Get(&prefs) + exists, err := x.Where("org_id=? AND user_id=? AND team_id=?", query.OrgId, query.UserId, query.TeamId).Get(&prefs) if err != nil { return err @@ -71,7 +82,7 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { return inTransaction(func(sess *DBSession) error { var prefs m.Preferences - exists, err := sess.Where("org_id=? AND user_id=?", cmd.OrgId, cmd.UserId).Get(&prefs) + 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 } @@ -80,6 +91,7 @@ func SavePreferences(cmd *m.SavePreferencesCommand) error { prefs = m.Preferences{ UserId: cmd.UserId, OrgId: cmd.OrgId, + TeamId: cmd.TeamId, HomeDashboardId: cmd.HomeDashboardId, Timezone: cmd.Timezone, Theme: cmd.Theme, diff --git a/pkg/services/sqlstore/preferences_test.go b/pkg/services/sqlstore/preferences_test.go new file mode 100644 index 00000000000..f9a839bf5a7 --- /dev/null +++ b/pkg/services/sqlstore/preferences_test.go @@ -0,0 +1,91 @@ +package sqlstore + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" +) + +func TestPreferencesDataAccess(t *testing.T) { + Convey("Testing preferences data access", t, func() { + InitTestDB(t) + + Convey("GetPreferencesWithDefaults with no saved preferences should return defaults", func() { + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.Theme, ShouldEqual, setting.DefaultTheme) + So(query.Result.Timezone, ShouldEqual, "browser") + So(query.Result.HomeDashboardId, ShouldEqual, 0) + }) + + Convey("GetPreferencesWithDefaults with saved org and user home dashboard should return user home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, UserId: 1, HomeDashboardId: 4}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 1}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 4) + }) + + Convey("GetPreferencesWithDefaults with saved org and other user home dashboard should return org home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, UserId: 1, HomeDashboardId: 4}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 2}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 1) + }) + + Convey("GetPreferencesWithDefaults with saved org and teams home dashboard should return last team home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 2, HomeDashboardId: 2}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 3, HomeDashboardId: 3}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, Teams: []int64{2, 3}}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 3) + }) + + Convey("GetPreferencesWithDefaults with saved org and other teams home dashboard should return org home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 2, HomeDashboardId: 2}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 3, HomeDashboardId: 3}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 1) + }) + + Convey("GetPreferencesWithDefaults with saved org, teams and user home dashboard should return user home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 2, HomeDashboardId: 2}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 3, HomeDashboardId: 3}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, UserId: 1, HomeDashboardId: 4}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 1, Teams: []int64{2, 3}}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 4) + }) + + Convey("GetPreferencesWithDefaults with saved org, other teams and user home dashboard should return org home dashboard", func() { + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, HomeDashboardId: 1}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 2, HomeDashboardId: 2}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, TeamId: 3, HomeDashboardId: 3}) + SavePreferences(&models.SavePreferencesCommand{OrgId: 1, UserId: 1, HomeDashboardId: 4}) + + query := &models.GetPreferencesWithDefaultsQuery{User: &models.SignedInUser{OrgId: 1, UserId: 2}} + err := GetPreferencesWithDefaults(query) + So(err, ShouldBeNil) + So(query.Result.HomeDashboardId, ShouldEqual, 1) + }) + }) +}