From c684b1ddabd17d08a5dc4d11fb8f040b1bcc3bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 22 Aug 2014 15:32:42 +0200 Subject: [PATCH] dashboard load/save work and began work on register --- grafana | 2 +- pkg/api/api_dashboard.go | 6 ++-- pkg/api/api_login.go | 1 + pkg/models/dashboard_test.go | 18 ++++++++++ pkg/models/dashboards.go | 19 +++++++--- pkg/stores/rethinkdb.go | 25 ++++++++++---- pkg/stores/rethinkdb_accounts.go | 59 ++++++++++++++++++++++++++++++++ pkg/stores/rethinkdb_test.go | 32 ++++++++++++++--- pkg/stores/store.go | 2 +- 9 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 pkg/models/dashboard_test.go create mode 100644 pkg/stores/rethinkdb_accounts.go diff --git a/grafana b/grafana index 06de80f2a76..477f66f56bd 160000 --- a/grafana +++ b/grafana @@ -1 +1 @@ -Subproject commit 06de80f2a764b76df6047fc4700c74aaf5734521 +Subproject commit 477f66f56bdd7f310d439ac428cb646fbb9b1949 diff --git a/pkg/api/api_dashboard.go b/pkg/api/api_dashboard.go index 5e2ca7c96d1..b2374f2e096 100644 --- a/pkg/api/api_dashboard.go +++ b/pkg/api/api_dashboard.go @@ -17,7 +17,7 @@ func init() { func (self *HttpServer) getDashboard(c *gin.Context) { id := c.Params.ByName("id") - dash, err := self.store.GetDashboardByTitle(id, "test") + dash, err := self.store.GetDashboard(id, 1) if err != nil { c.JSON(404, newErrorResponse("Dashboard not found")) return @@ -46,6 +46,8 @@ func (self *HttpServer) postDashboard(c *gin.Context) { dashboard := models.NewDashboard("test") dashboard.Data = command.Dashboard dashboard.Title = dashboard.Data["title"].(string) + dashboard.AccountId = 1 + dashboard.UpdateSlug() if dashboard.Data["id"] != nil { dashboard.Id = dashboard.Data["id"].(string) @@ -53,7 +55,7 @@ func (self *HttpServer) postDashboard(c *gin.Context) { err := self.store.SaveDashboard(dashboard) if err == nil { - c.JSON(200, gin.H{"status": "success", "id": dashboard.Id}) + c.JSON(200, gin.H{"status": "success", "slug": dashboard.Slug}) return } } diff --git a/pkg/api/api_login.go b/pkg/api/api_login.go index 284af4ff1d5..c722f0cdeb2 100644 --- a/pkg/api/api_login.go +++ b/pkg/api/api_login.go @@ -5,6 +5,7 @@ import "github.com/gin-gonic/gin" func init() { addRoutes(func(self *HttpServer) { self.router.GET("/login/*_", self.index) + self.router.GET("/register/*_", self.index) self.router.POST("/login", self.loginPost) self.router.POST("/logout", self.logoutPost) }) diff --git a/pkg/models/dashboard_test.go b/pkg/models/dashboard_test.go new file mode 100644 index 00000000000..0828e51480f --- /dev/null +++ b/pkg/models/dashboard_test.go @@ -0,0 +1,18 @@ +package models + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestDashboardModel(t *testing.T) { + + Convey("When generating slug", t, func() { + dashboard := NewDashboard("Grafana Play Home") + dashboard.UpdateSlug() + + So(dashboard.Slug, ShouldEqual, "grafana-play-home") + }) + +} diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 82ce602c136..a2d733d4053 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -3,12 +3,15 @@ package models import ( "encoding/json" "io" + "regexp" + "strings" "time" ) type Dashboard struct { Id string `gorethink:"id,omitempty"` - AccountId string + Slug string + AccountId int LastModifiedByUserId string LastModifiedByDate time.Time CreatedDate time.Time @@ -26,18 +29,19 @@ type UserContext struct { type SearchResult struct { Id string `json:"id"` Title string `json:"title"` + Slug string `json:"slug"` } func NewDashboard(title string) *Dashboard { dash := &Dashboard{} dash.Id = "" - dash.AccountId = "test" dash.LastModifiedByDate = time.Now() dash.CreatedDate = time.Now() dash.LastModifiedByUserId = "123" - dash.Title = title dash.Data = make(map[string]interface{}) dash.Data["title"] = title + dash.Title = title + dash.UpdateSlug() return dash } @@ -50,11 +54,16 @@ func NewFromJson(reader io.Reader) (*Dashboard, error) { return nil, err } - dash.Title = dash.Data["title"].(string) - return dash, nil } func (dash *Dashboard) GetString(prop string) string { return dash.Data[prop].(string) } + +func (dash *Dashboard) UpdateSlug() { + title := strings.ToLower(dash.Data["title"].(string)) + re := regexp.MustCompile("[^\\w ]+") + re2 := regexp.MustCompile("\\s") + dash.Slug = re2.ReplaceAllString(re.ReplaceAllString(title, ""), "-") +} diff --git a/pkg/stores/rethinkdb.go b/pkg/stores/rethinkdb.go index ad0f277501f..4d9d7864f16 100644 --- a/pkg/stores/rethinkdb.go +++ b/pkg/stores/rethinkdb.go @@ -16,6 +16,11 @@ type RethinkCfg struct { DatabaseName string } +type Account struct { + Id int `gorethink:"id"` + NextDashboardId int +} + func NewRethinkStore(config *RethinkCfg) *rethinkStore { log.Info("Initializing rethink storage") @@ -32,17 +37,24 @@ func NewRethinkStore(config *RethinkCfg) *rethinkStore { r.DbCreate(config.DatabaseName).Exec(session) r.Db(config.DatabaseName).TableCreate("dashboards").Exec(session) - r.Db(config.DatabaseName).Table("dashboards").IndexCreateFunc("AccountIdTitle", func(row r.Term) interface{} { - return []interface{}{row.Field("AccountId"), row.Field("Title")} + r.Db(config.DatabaseName).TableCreate("accounts").Exec(session) + r.Db(config.DatabaseName).TableCreate("master").Exec(session) + r.Db(config.DatabaseName).Table("dashboards").IndexCreateFunc("AccountIdSlug", func(row r.Term) interface{} { + return []interface{}{row.Field("AccountId"), row.Field("Slug")} }).Exec(session) + _, err = r.Table("master").Insert(map[string]interface{}{"id": "ids", "NextAccountId": 0}).RunWrite(session) + if err != nil { + log.Error("Failed to insert master ids row", err) + } + return &rethinkStore{ session: session, } } func (self *rethinkStore) SaveDashboard(dash *models.Dashboard) error { - resp, err := r.Table("dashboards").Insert(dash).RunWrite(self.session) + resp, err := r.Table("dashboards").Insert(dash, r.InsertOpts{Upsert: true}).RunWrite(self.session) if err != nil { return err } @@ -56,8 +68,8 @@ func (self *rethinkStore) SaveDashboard(dash *models.Dashboard) error { return nil } -func (self *rethinkStore) GetDashboardByTitle(title string, accountId string) (*models.Dashboard, error) { - resp, err := r.Table("dashboards").GetAllByIndex("AccountIdTitle", []interface{}{accountId, title}).Run(self.session) +func (self *rethinkStore) GetDashboard(slug string, accountId int) (*models.Dashboard, error) { + resp, err := r.Table("dashboards").GetAllByIndex("AccountIdSlug", []interface{}{accountId, slug}).Run(self.session) if err != nil { return nil, err } @@ -81,10 +93,9 @@ func (self *rethinkStore) Query(query string) ([]*models.SearchResult, error) { results := make([]*models.SearchResult, 0, 50) var dashboard models.Dashboard for docs.Next(&dashboard) { - log.Info("title: ", dashboard.Title) results = append(results, &models.SearchResult{ Title: dashboard.Title, - Id: dashboard.Id, + Id: dashboard.Slug, }) } diff --git a/pkg/stores/rethinkdb_accounts.go b/pkg/stores/rethinkdb_accounts.go new file mode 100644 index 00000000000..60905d8ca8e --- /dev/null +++ b/pkg/stores/rethinkdb_accounts.go @@ -0,0 +1,59 @@ +package stores + +import ( + "errors" + + r "github.com/dancannon/gorethink" +) + +func (self *rethinkStore) getNextAccountId() (int, error) { + resp, err := r.Table("master").Get("ids").Update(map[string]interface{}{ + "NextAccountId": r.Row.Field("NextAccountId").Add(1), + }, r.UpdateOpts{ReturnVals: true}).RunWrite(self.session) + + if err != nil { + return 0, err + } + + if resp.NewValue == nil { + return 0, errors.New("Failed to get new value after incrementing account id") + } + + return int(resp.NewValue.(map[string]interface{})["NextAccountId"].(float64)), nil +} + +func (self *rethinkStore) createAccount() (*Account, error) { + accountId, err := self.getNextAccountId() + if err != nil { + return nil, err + } + + account := &Account{Id: accountId, NextDashboardId: 0} + + resp, err := r.Table("accounts").Insert(account).RunWrite(self.session) + if err != nil { + return nil, err + } + + if resp.Inserted == 0 { + return nil, errors.New("Failed to insert acccount") + } + + return account, nil +} + +func (self *rethinkStore) getNextDashboardNumber(accountId int) (int, error) { + resp, err := r.Table("accounts").Get(accountId).Update(map[string]interface{}{ + "NextDashboardId": r.Row.Field("NextDashboardId").Add(1), + }, r.UpdateOpts{ReturnVals: true}).RunWrite(self.session) + + if err != nil { + return 0, err + } + + if resp.NewValue == nil { + return 0, errors.New("Failed to get next dashboard id, no new value after update") + } + + return int(resp.NewValue.(map[string]interface{})["NextDashboardId"].(float64)), nil +} diff --git a/pkg/stores/rethinkdb_test.go b/pkg/stores/rethinkdb_test.go index aa6e7d99684..b7766671e4b 100644 --- a/pkg/stores/rethinkdb_test.go +++ b/pkg/stores/rethinkdb_test.go @@ -3,27 +3,49 @@ package stores import ( "testing" + "github.com/dancannon/gorethink" . "github.com/smartystreets/goconvey/convey" "github.com/torkelo/grafana-pro/pkg/models" ) func TestRethinkStore(t *testing.T) { + store := NewRethinkStore(&RethinkCfg{DatabaseName: "tests"}) + defer gorethink.DbDrop("tests").Exec(store.session) Convey("Insert dashboard", t, func() { - store := NewRethinkStore(&RethinkCfg{DatabaseName: "tests"}) - //defer r.DbDrop("tests").Exec(store.session) - dashboard := models.NewDashboard("test") - dashboard.AccountId = "123" + dashboard.AccountId = 1 err := store.SaveDashboard(dashboard) So(err, ShouldBeNil) So(dashboard.Id, ShouldNotBeEmpty) - read, err := store.GetDashboardByTitle("test", "123") + read, err := store.GetDashboard("test", 1) So(err, ShouldBeNil) So(read, ShouldNotBeNil) + }) + Convey("can get next account id", t, func() { + id, err := store.getNextAccountId() + So(err, ShouldBeNil) + So(id, ShouldNotEqual, 0) + + id2, err := store.getNextAccountId() + So(id2, ShouldEqual, id+1) + }) + + Convey("can create account", t, func() { + account, err := store.createAccount() + So(err, ShouldBeNil) + So(account, ShouldNotBeNil) + So(account.Id, ShouldNotEqual, 0) + }) + + Convey("can get next dashboard id", t, func() { + account, err := store.createAccount() + dashId, err := store.getNextDashboardNumber(account.Id) + So(err, ShouldBeNil) + So(dashId, ShouldEqual, 1) }) } diff --git a/pkg/stores/store.go b/pkg/stores/store.go index 63fb3ca4e29..7b233e72294 100644 --- a/pkg/stores/store.go +++ b/pkg/stores/store.go @@ -5,7 +5,7 @@ import ( ) type Store interface { - GetDashboardByTitle(id string, accountId string) (*models.Dashboard, error) + GetDashboard(title string, accountId int) (*models.Dashboard, error) SaveDashboard(dash *models.Dashboard) error Query(query string) ([]*models.SearchResult, error) Close()