diff --git a/grafana b/grafana index 66d9c4f1afb..1e3970c6e56 160000 --- a/grafana +++ b/grafana @@ -1 +1 @@ -Subproject commit 66d9c4f1afb689eb2a1640561a06cb166e28c062 +Subproject commit 1e3970c6e56af810047ab815acd7fd5e681b5139 diff --git a/pkg/api/api.go b/pkg/api/api.go index 0acd05d704d..922a913bade 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -45,8 +45,8 @@ func Register(r *macaron.Macaron) { r.Put("/", bind(m.UpdateUserCommand{}), UpdateUser) r.Post("/using/:id", SetUsingAccount) r.Get("/accounts", GetUserAccounts) - r.Post("/favorites/dashboard/:id", AddAsFavorite) - r.Delete("/favorites/dashboard/:id", RemoveAsFavorite) + r.Post("/stars/dashboard/:id", StarDashboard) + r.Delete("/stars/dashboard/:id", UnstarDashboard) }) // account diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index ec99c435dfa..187fff32933 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -8,6 +8,19 @@ import ( "github.com/torkelo/grafana-pro/pkg/util" ) +func isDasboardStarredByUser(c *middleware.Context, dashId int64) (bool, error) { + if !c.IsSignedIn { + return false, nil + } + + query := m.IsStarredByUserQuery{UserId: c.UserId, DashboardId: dashId} + if err := bus.Dispatch(&query); err != nil { + return false, err + } + + return query.Result, nil +} + func GetDashboard(c *middleware.Context) { slug := c.Params(":slug") @@ -18,10 +31,16 @@ func GetDashboard(c *middleware.Context) { return } + isStarred, err := isDasboardStarredByUser(c, query.Result.Id) + if err != nil { + c.JsonApiErr(500, "Error while checking if dashboard was starred by user", err) + return + } + dash := query.Result dto := dtos.Dashboard{ - IsFavorite: false, - Dashboard: dash.Data, + Model: dash.Data, + Meta: dtos.DashboardMeta{IsStarred: isStarred}, } c.JSON(200, dto) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index d14365a41cc..e94015ccf97 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -25,9 +25,13 @@ type CurrentUser struct { GravatarUrl string `json:"gravatarUrl"` } +type DashboardMeta struct { + IsStarred bool `json:"isStarred"` +} + type Dashboard struct { - IsFavorite bool `json:"isFavorite"` - Dashboard map[string]interface{} `json:"dashboard"` + Meta DashboardMeta `json:"meta"` + Model map[string]interface{} `json:"model"` } type DataSource struct { diff --git a/pkg/api/favorite.go b/pkg/api/favorite.go deleted file mode 100644 index 180315567ab..00000000000 --- a/pkg/api/favorite.go +++ /dev/null @@ -1,35 +0,0 @@ -package api - -import ( - "github.com/torkelo/grafana-pro/pkg/bus" - "github.com/torkelo/grafana-pro/pkg/middleware" - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func AddAsFavorite(c *middleware.Context) { - var cmd = m.AddAsFavoriteCommand{ - UserId: c.UserId, - DashboardId: c.ParamsInt64(":id"), - } - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed to add favorite", err) - return - } - - c.JsonOK("Dashboard marked as favorite") -} - -func RemoveAsFavorite(c *middleware.Context) { - var cmd = m.RemoveAsFavoriteCommand{ - UserId: c.UserId, - DashboardId: c.ParamsInt64(":id"), - } - - if err := bus.Dispatch(&cmd); err != nil { - c.JsonApiErr(500, "Failed to remove favorite", err) - return - } - - c.JsonOK("Favorite removed") -} diff --git a/pkg/api/stars.go b/pkg/api/stars.go new file mode 100644 index 00000000000..20436302ccd --- /dev/null +++ b/pkg/api/stars.go @@ -0,0 +1,45 @@ +package api + +import ( + "github.com/torkelo/grafana-pro/pkg/bus" + "github.com/torkelo/grafana-pro/pkg/middleware" + m "github.com/torkelo/grafana-pro/pkg/models" +) + +func StarDashboard(c *middleware.Context) { + var cmd = m.StarDashboardCommand{ + UserId: c.UserId, + DashboardId: c.ParamsInt64(":id"), + } + + if cmd.DashboardId <= 0 { + c.JsonApiErr(400, "Missing dashboard id", nil) + return + } + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to star dashboard", err) + return + } + + c.JsonOK("Dashboard starred!") +} + +func UnstarDashboard(c *middleware.Context) { + var cmd = m.UnstarDashboardCommand{ + UserId: c.UserId, + DashboardId: c.ParamsInt64(":id"), + } + + if cmd.DashboardId <= 0 { + c.JsonApiErr(400, "Missing dashboard id", nil) + return + } + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to unstar dashboard", err) + return + } + + c.JsonOK("Dashboard unstarred") +} diff --git a/pkg/models/favorite.go b/pkg/models/favorite.go deleted file mode 100644 index a38d256b8de..00000000000 --- a/pkg/models/favorite.go +++ /dev/null @@ -1,29 +0,0 @@ -package models - -type Favorite struct { - Id int64 - UserId int64 - DashboardId int64 -} - -// ---------------------- -// COMMANDS - -type AddAsFavoriteCommand struct { - UserId int64 - DashboardId int64 -} - -type RemoveAsFavoriteCommand struct { - UserId int64 - DashboardId int64 -} - -// --------------------- -// QUERIES - -type GetUserFavoritesQuery struct { - UserId int64 - - Result []Favorite -} diff --git a/pkg/models/star.go b/pkg/models/star.go new file mode 100644 index 00000000000..74d46c1b844 --- /dev/null +++ b/pkg/models/star.go @@ -0,0 +1,40 @@ +package models + +import "errors" + +var ErrCommandValidationFailed = errors.New("Command missing required fields") + +type Star struct { + Id int64 + UserId int64 + DashboardId int64 +} + +// ---------------------- +// COMMANDS + +type StarDashboardCommand struct { + UserId int64 + DashboardId int64 +} + +type UnstarDashboardCommand struct { + UserId int64 + DashboardId int64 +} + +// --------------------- +// QUERIES + +type GetUserStarsQuery struct { + UserId int64 + + Result []Star +} + +type IsStarredByUserQuery struct { + UserId int64 + DashboardId int64 + + Result bool +} diff --git a/pkg/services/sqlstore/favorite.go b/pkg/services/sqlstore/favorite.go deleted file mode 100644 index 4455559521f..00000000000 --- a/pkg/services/sqlstore/favorite.go +++ /dev/null @@ -1,41 +0,0 @@ -package sqlstore - -import ( - "github.com/go-xorm/xorm" - - "github.com/torkelo/grafana-pro/pkg/bus" - m "github.com/torkelo/grafana-pro/pkg/models" -) - -func init() { - bus.AddHandler("sql", AddAsFavorite) - bus.AddHandler("sql", RemoveAsFavorite) - bus.AddHandler("sql", GetUserFavorites) -} - -func AddAsFavorite(cmd *m.AddAsFavoriteCommand) error { - return inTransaction(func(sess *xorm.Session) error { - - entity := m.Favorite{ - UserId: cmd.UserId, - DashboardId: cmd.DashboardId, - } - - _, err := sess.Insert(&entity) - return err - }) -} - -func RemoveAsFavorite(cmd *m.RemoveAsFavoriteCommand) error { - return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM favorite WHERE user_id=? and dashboard_id=?" - _, err := sess.Exec(rawSql, cmd.UserId, cmd.DashboardId) - return err - }) -} - -func GetUserFavorites(query *m.GetUserFavoritesQuery) error { - query.Result = make([]m.Favorite, 0) - err := x.Where("user_id=?", query.UserId).Find(&query.Result) - return err -} diff --git a/pkg/services/sqlstore/migrations.go b/pkg/services/sqlstore/migrations.go index 7ee0aa6c089..6948477cc59 100644 --- a/pkg/services/sqlstore/migrations.go +++ b/pkg/services/sqlstore/migrations.go @@ -10,7 +10,7 @@ import . "github.com/torkelo/grafana-pro/pkg/services/sqlstore/migrator" func addMigrations(mg *Migrator) { addMigrationLogMigrations(mg) addUserMigrations(mg) - addFavoritesMigrations(mg) + addStarMigrations(mg) addAccountMigrations(mg) addDashboardMigration(mg) addDataSourceMigration(mg) @@ -55,16 +55,16 @@ func addUserMigrations(mg *Migrator) { Table("user").Column(&Column{Name: "rands", Type: DB_NVarchar, Length: 255, Nullable: true})) } -func addFavoritesMigrations(mg *Migrator) { - mg.AddMigration("create favorite table", new(AddTableMigration). - Name("favorite").WithColumns( +func addStarMigrations(mg *Migrator) { + mg.AddMigration("create star table", new(AddTableMigration). + Name("star").WithColumns( &Column{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, &Column{Name: "user_id", Type: DB_BigInt, Nullable: false}, &Column{Name: "dashboard_id", Type: DB_BigInt, Nullable: false}, )) - mg.AddMigration("add unique index favorite.user_id_dashboard_id", new(AddIndexMigration). - Table("favorite").Columns("user_id", "dashboard_id").Unique()) + mg.AddMigration("add unique index star.user_id_dashboard_id", new(AddIndexMigration). + Table("star").Columns("user_id", "dashboard_id").Unique()) } func addAccountMigrations(mg *Migrator) { diff --git a/pkg/services/sqlstore/star.go b/pkg/services/sqlstore/star.go new file mode 100644 index 00000000000..8baeb89e274 --- /dev/null +++ b/pkg/services/sqlstore/star.go @@ -0,0 +1,68 @@ +package sqlstore + +import ( + "strconv" + + "github.com/go-xorm/xorm" + + "github.com/torkelo/grafana-pro/pkg/bus" + m "github.com/torkelo/grafana-pro/pkg/models" +) + +func init() { + bus.AddHandler("sql", StarDashboard) + bus.AddHandler("sql", UnstarDashboard) + bus.AddHandler("sql", GetUserStars) + bus.AddHandler("sql", IsStarredByUser) +} + +func IsStarredByUser(query *m.IsStarredByUserQuery) error { + rawSql := "SELECT 1 from star where user_id=? and dashboard_id=?" + results, err := x.Query(rawSql, query.UserId, query.DashboardId) + + if err != nil { + return err + } + if len(results) == 0 { + return nil + } + + query.Result, _ = strconv.ParseBool(string(results[0]["1"])) + + return nil +} + +func StarDashboard(cmd *m.StarDashboardCommand) error { + if cmd.DashboardId == 0 || cmd.UserId == 0 { + return m.ErrCommandValidationFailed + } + + return inTransaction(func(sess *xorm.Session) error { + + entity := m.Star{ + UserId: cmd.UserId, + DashboardId: cmd.DashboardId, + } + + _, err := sess.Insert(&entity) + return err + }) +} + +func UnstarDashboard(cmd *m.UnstarDashboardCommand) error { + if cmd.DashboardId == 0 || cmd.UserId == 0 { + return m.ErrCommandValidationFailed + } + + return inTransaction(func(sess *xorm.Session) error { + var rawSql = "DELETE FROM star WHERE user_id=? and dashboard_id=?" + _, err := sess.Exec(rawSql, cmd.UserId, cmd.DashboardId) + return err + }) +} + +func GetUserStars(query *m.GetUserStarsQuery) error { + query.Result = make([]m.Star, 0) + err := x.Where("user_id=?", query.UserId).Find(&query.Result) + return err +} diff --git a/pkg/services/sqlstore/stars_test.go b/pkg/services/sqlstore/stars_test.go new file mode 100644 index 00000000000..23b2eaf6502 --- /dev/null +++ b/pkg/services/sqlstore/stars_test.go @@ -0,0 +1,42 @@ +package sqlstore + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + m "github.com/torkelo/grafana-pro/pkg/models" +) + +func TestUserStarsDataAccess(t *testing.T) { + + Convey("Testing User Stars Data Access", t, func() { + InitTestDB(t) + + Convey("Given saved star", func() { + cmd := m.StarDashboardCommand{ + DashboardId: 10, + UserId: 12, + } + + err := StarDashboard(&cmd) + So(err, ShouldBeNil) + + Convey("IsStarredByUser should return true when starred", func() { + query := m.IsStarredByUserQuery{UserId: 12, DashboardId: 10} + err := IsStarredByUser(&query) + So(err, ShouldBeNil) + + So(query.Result, ShouldBeTrue) + }) + + Convey("IsStarredByUser should return false when not starred", func() { + query := m.IsStarredByUserQuery{UserId: 12, DashboardId: 12} + err := IsStarredByUser(&query) + So(err, ShouldBeNil) + + So(query.Result, ShouldBeFalse) + }) + + }) + }) +}