diff --git a/grafana b/grafana index df51be02bf4..882ee4d49f1 160000 --- a/grafana +++ b/grafana @@ -1 +1 @@ -Subproject commit df51be02bf4fa72ac4f1bcb15006f08f8b93ce8d +Subproject commit 882ee4d49f1df4550e898a914b4d17d40add4ddf diff --git a/pkg/api/search.go b/pkg/api/search.go index 83f8a3fcc73..9d1765c3002 100644 --- a/pkg/api/search.go +++ b/pkg/api/search.go @@ -7,10 +7,13 @@ import ( "github.com/torkelo/grafana-pro/pkg/bus" "github.com/torkelo/grafana-pro/pkg/middleware" m "github.com/torkelo/grafana-pro/pkg/models" + "github.com/torkelo/grafana-pro/pkg/setting" ) func Search(c *middleware.Context) { queryText := c.Query("q") + starred := c.Query("starred") + result := m.SearchResult{ Dashboards: []*m.DashboardSearchHit{}, Tags: []*m.DashboardTagCloudItem{}, @@ -31,6 +34,8 @@ func Search(c *middleware.Context) { query := m.SearchDashboardsQuery{ Title: matches[3], Tag: matches[2], + UserId: c.UserId, + IsStarred: starred == "1", AccountId: c.AccountId, } err := bus.Dispatch(&query) @@ -38,7 +43,11 @@ func Search(c *middleware.Context) { c.JsonApiErr(500, "Search failed", err) return } + result.Dashboards = query.Result + for _, dash := range result.Dashboards { + dash.Url = setting.AbsUrlTo("dashboard/db/" + dash.Slug) + } } c.JSON(200, result) diff --git a/pkg/models/search.go b/pkg/models/search.go index 272c87fa5ec..9fe6ab7a5ae 100644 --- a/pkg/models/search.go +++ b/pkg/models/search.go @@ -11,6 +11,7 @@ type DashboardSearchHit struct { Title string `json:"title"` Slug string `json:"slug"` Tags []string `json:"tags"` + Url string `json:"url"` } type DashboardTagCloudItem struct { @@ -22,6 +23,8 @@ type SearchDashboardsQuery struct { Title string Tag string AccountId int64 + UserId int64 + IsStarred bool Result []*DashboardSearchHit } diff --git a/pkg/services/sqlstore/account_users.go b/pkg/services/sqlstore/account_users.go index 52bce85e52d..41359075ceb 100644 --- a/pkg/services/sqlstore/account_users.go +++ b/pkg/services/sqlstore/account_users.go @@ -1,6 +1,7 @@ package sqlstore import ( + "fmt" "time" "github.com/go-xorm/xorm" @@ -34,7 +35,7 @@ func AddAccountUser(cmd *m.AddAccountUserCommand) error { func GetAccountUsers(query *m.GetAccountUsersQuery) error { query.Result = make([]*m.AccountUserDTO, 0) sess := x.Table("account_user") - sess.Join("INNER", "user", "account_user.user_id=user.id") + sess.Join("INNER", "user", fmt.Sprintf("account_user.user_id=%s.id", x.Dialect().Quote("user"))) sess.Where("account_user.account_id=?", query.AccountId) sess.Cols("account_user.account_id", "account_user.user_id", "user.email", "user.login", "account_user.role") sess.Asc("user.email", "user.login") diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 1cccc07eb6a..b25c6774460 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -1,6 +1,8 @@ package sqlstore import ( + "bytes" + "github.com/go-xorm/xorm" "github.com/torkelo/grafana-pro/pkg/bus" m "github.com/torkelo/grafana-pro/pkg/models" @@ -80,20 +82,42 @@ type DashboardSearchProjection struct { } func SearchDashboards(query *m.SearchDashboardsQuery) error { - titleQuery := "%" + query.Title + "%" + var sql bytes.Buffer + params := make([]interface{}, 0) - sess := x.Table("dashboard") - sess.Join("LEFT OUTER", "dashboard_tag", "dashboard.id=dashboard_tag.dashboard_id") - sess.Where("account_id=? AND title LIKE ?", query.AccountId, titleQuery) - sess.Cols("dashboard.id", "dashboard.title", "dashboard.slug", "dashboard_tag.term") - sess.Limit(100, 0) + sql.WriteString(`SELECT + dashboard.id, + dashboard.title, + dashboard.slug, + dashboard_tag.term + FROM dashboard + LEFT OUTER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id`) + + if query.IsStarred { + sql.WriteString(" INNER JOIN star on star.dashboard_id = dashboard.id") + } + + sql.WriteString(` WHERE dashboard.account_id=?`) + + params = append(params, query.AccountId) + + if query.IsStarred { + sql.WriteString(` AND star.user_id=?`) + params = append(params, query.UserId) + } + + if len(query.Title) > 0 { + sql.WriteString(" AND dashboard.title LIKE ?") + params = append(params, "%"+query.Title+"%") + } if len(query.Tag) > 0 { - sess.And("dashboard_tag.term=?", query.Tag) + sql.WriteString(" AND dashboard_tag.term=?") + params = append(params, query.Tag) } var res []DashboardSearchProjection - err := sess.Find(&res) + err := x.Sql(sql.String(), params...).Find(&res) if err != nil { return err } diff --git a/pkg/services/sqlstore/dashboard_test.go b/pkg/services/sqlstore/dashboard_test.go index 9135ab60eff..5c878130136 100644 --- a/pkg/services/sqlstore/dashboard_test.go +++ b/pkg/services/sqlstore/dashboard_test.go @@ -8,27 +8,29 @@ import ( m "github.com/torkelo/grafana-pro/pkg/models" ) +func insertTestDashboard(title string, accountId int64, tags ...interface{}) *m.Dashboard { + cmd := m.SaveDashboardCommand{ + AccountId: accountId, + Dashboard: map[string]interface{}{ + "id": nil, + "title": title, + "tags": tags, + }, + } + + err := SaveDashboard(&cmd) + So(err, ShouldBeNil) + + return cmd.Result +} + func TestDashboardDataAccess(t *testing.T) { Convey("Testing DB", t, func() { InitTestDB(t) Convey("Given saved dashboard", func() { - var savedDash *m.Dashboard - - cmd := m.SaveDashboardCommand{ - AccountId: 1, - Dashboard: map[string]interface{}{ - "id": nil, - "title": "test dash 23", - "tags": []interface{}{"prod", "webapp"}, - }, - } - - err := SaveDashboard(&cmd) - So(err, ShouldBeNil) - - savedDash = cmd.Result + savedDash := insertTestDashboard("test dash 23", 1, "prod", "webapp") Convey("Should return dashboard model", func() { So(savedDash.Title, ShouldEqual, "test dash 23") @@ -97,6 +99,28 @@ func TestDashboardDataAccess(t *testing.T) { So(len(query.Result), ShouldEqual, 2) }) + + Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() { + starredDash := insertTestDashboard("starred dash", 1) + StarDashboard(&m.StarDashboardCommand{ + DashboardId: starredDash.Id, + UserId: 10, + }) + + StarDashboard(&m.StarDashboardCommand{ + DashboardId: savedDash.Id, + UserId: 1, + }) + + Convey("Should be able to search for starred dashboards", func() { + query := m.SearchDashboardsQuery{AccountId: 1, UserId: 10, IsStarred: true} + err := SearchDashboards(&query) + + So(err, ShouldBeNil) + So(len(query.Result), ShouldEqual, 1) + So(query.Result[0].Title, ShouldEqual, "starred dash") + }) + }) }) }) } diff --git a/pkg/services/sqlstore/migrations.go b/pkg/services/sqlstore/migrations.go index 6948477cc59..f18e55a7f1b 100644 --- a/pkg/services/sqlstore/migrations.go +++ b/pkg/services/sqlstore/migrations.go @@ -36,7 +36,7 @@ func addUserMigrations(mg *Migrator) { &Column{Name: "login", Type: DB_NVarchar, Length: 255, Nullable: false}, &Column{Name: "email", Type: DB_NVarchar, Length: 255, Nullable: false}, &Column{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: true}, - &Column{Name: "password", Type: DB_NVarchar, Length: 50, Nullable: true}, + &Column{Name: "password", Type: DB_NVarchar, Length: 255, Nullable: true}, &Column{Name: "salt", Type: DB_NVarchar, Length: 50, Nullable: true}, &Column{Name: "company", Type: DB_NVarchar, Length: 255, Nullable: true}, &Column{Name: "account_id", Type: DB_BigInt, Nullable: false}, diff --git a/pkg/services/sqlstore/migrator/dialect.go b/pkg/services/sqlstore/migrator/dialect.go index 00080d08477..803e8eafccc 100644 --- a/pkg/services/sqlstore/migrator/dialect.go +++ b/pkg/services/sqlstore/migrator/dialect.go @@ -95,7 +95,7 @@ func (b *BaseDialect) CreateTableSql(table *Table) string { } func (db *BaseDialect) AddColumnSql(tableName string, col *Column) string { - return fmt.Sprintf("alter table %s ADD COLUMN %s", tableName, col.StringNoPk(db.dialect)) + return fmt.Sprintf("alter table %s ADD COLUMN %s", db.dialect.Quote(tableName), col.StringNoPk(db.dialect)) } func (db *BaseDialect) CreateIndexSql(tableName string, index *Index) string { diff --git a/pkg/services/sqlstore/star.go b/pkg/services/sqlstore/star.go index 8baeb89e274..07ae1da5e7c 100644 --- a/pkg/services/sqlstore/star.go +++ b/pkg/services/sqlstore/star.go @@ -1,8 +1,6 @@ package sqlstore import ( - "strconv" - "github.com/go-xorm/xorm" "github.com/torkelo/grafana-pro/pkg/bus" @@ -23,11 +21,12 @@ func IsStarredByUser(query *m.IsStarredByUserQuery) error { if err != nil { return err } + if len(results) == 0 { return nil } - query.Result, _ = strconv.ParseBool(string(results[0]["1"])) + query.Result = true return nil } diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index 67a78aa3ec6..0d68070e82f 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -69,9 +69,11 @@ func CreateUser(cmd *m.CreateUserCommand) error { Updated: time.Now(), } - user.Salt = util.GetRandomString(10) - user.Rands = util.GetRandomString(10) - user.Password = util.EncodePassword(cmd.Password, user.Salt) + if len(cmd.Password) > 0 { + user.Salt = util.GetRandomString(10) + user.Rands = util.GetRandomString(10) + user.Password = util.EncodePassword(cmd.Password, user.Salt) + } sess.UseBool("is_admin") diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 3b65790295e..4464befc310 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -148,6 +148,10 @@ func parseAppUrlAndSubUrl(section *ini.Section) (string, string) { return appUrl, appSubUrl } +func AbsUrlTo(relativeUrl string) string { + return AppUrl + relativeUrl +} + func NewConfigContext() { configFiles := findConfigFiles()