Admin flagged users, create a default admin user on startup if missing
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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", GetAccountInfo)
|
||||
bus.AddHandler("sql", GetOtherAccounts)
|
||||
bus.AddHandler("sql", CreateAccount)
|
||||
bus.AddHandler("sql", SetUsingAccount)
|
||||
bus.AddHandler("sql", GetAccountById)
|
||||
bus.AddHandler("sql", GetAccountByLogin)
|
||||
bus.AddHandler("sql", GetAccountByToken)
|
||||
bus.AddHandler("sql", AddCollaborator)
|
||||
bus.AddHandler("sql", RemoveCollaborator)
|
||||
bus.AddHandler("sql", SearchAccounts)
|
||||
}
|
||||
|
||||
func CreateAccount(cmd *m.CreateAccountCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
account := m.Account{
|
||||
Email: cmd.Email,
|
||||
Login: cmd.Login,
|
||||
Password: cmd.Password,
|
||||
Salt: cmd.Salt,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
sess.UseBool("is_admin")
|
||||
|
||||
_, err := sess.Insert(&account)
|
||||
cmd.Result = account
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func SetUsingAccount(cmd *m.SetUsingAccountCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
account := m.Account{}
|
||||
sess.Id(cmd.AccountId).Get(&account)
|
||||
|
||||
account.UsingAccountId = cmd.UsingAccountId
|
||||
_, err := sess.Id(account.Id).Update(&account)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetAccountInfo(query *m.GetAccountInfoQuery) error {
|
||||
var account m.Account
|
||||
has, err := x.Id(query.Id).Get(&account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
query.Result = m.AccountDTO{
|
||||
Name: account.Name,
|
||||
Email: account.Email,
|
||||
Collaborators: make([]*m.CollaboratorDTO, 0),
|
||||
}
|
||||
|
||||
sess := x.Table("collaborator")
|
||||
sess.Join("INNER", "account", "account.id=collaborator.collaborator_id")
|
||||
sess.Where("collaborator.account_id=?", query.Id)
|
||||
err = sess.Find(&query.Result.Collaborators)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func AddCollaborator(cmd *m.AddCollaboratorCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
|
||||
entity := m.Collaborator{
|
||||
AccountId: cmd.AccountId,
|
||||
CollaboratorId: cmd.CollaboratorId,
|
||||
Role: cmd.Role,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&entity)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetAccountById(query *m.GetAccountByIdQuery) error {
|
||||
var err error
|
||||
|
||||
var account m.Account
|
||||
has, err := x.Id(query.Id).Get(&account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if account.UsingAccountId == 0 {
|
||||
account.UsingAccountId = account.Id
|
||||
}
|
||||
|
||||
query.Result = &account
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAccountByToken(query *m.GetAccountByTokenQuery) error {
|
||||
var err error
|
||||
|
||||
var account m.Account
|
||||
sess := x.Join("INNER", "token", "token.account_id = account.id")
|
||||
sess.Omit("token.id", "token.account_id", "token.name", "token.token",
|
||||
"token.role", "token.updated", "token.created")
|
||||
has, err := sess.Where("token.token=?", query.Token).Get(&account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if account.UsingAccountId == 0 {
|
||||
account.UsingAccountId = account.Id
|
||||
}
|
||||
|
||||
query.Result = &account
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAccountByLogin(query *m.GetAccountByLoginQuery) error {
|
||||
account := new(m.Account)
|
||||
if strings.Contains(query.Login, "@") {
|
||||
account = &m.Account{Email: query.Login}
|
||||
} else {
|
||||
account = &m.Account{Login: strings.ToLower(query.Login)}
|
||||
}
|
||||
|
||||
has, err := x.Get(account)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrAccountNotFound
|
||||
}
|
||||
|
||||
if account.UsingAccountId == 0 {
|
||||
account.UsingAccountId = account.Id
|
||||
}
|
||||
|
||||
query.Result = account
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemoveCollaborator(cmd *m.RemoveCollaboratorCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM collaborator WHERE collaborator_id=? and account_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.CollaboratorId, cmd.AccountId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetOtherAccounts(query *m.GetOtherAccountsQuery) error {
|
||||
query.Result = make([]*m.OtherAccountDTO, 0)
|
||||
sess := x.Table("collaborator")
|
||||
sess.Join("INNER", "account", "collaborator.account_id=account.id")
|
||||
sess.Where("collaborator_id=?", query.AccountId)
|
||||
sess.Cols("collaborator.account_id", "collaborator.role", "account.email")
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
|
||||
func SearchAccounts(query *m.SearchAccountsQuery) error {
|
||||
query.Result = make([]*m.AccountSearchHitDTO, 0)
|
||||
sess := x.Table("account")
|
||||
sess.Where("email LIKE ?", query.Query+"%")
|
||||
sess.Limit(query.Limit, query.Limit*query.Page)
|
||||
sess.Cols("id", "email", "name")
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
func TestAccountDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Testing Account DB Access", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Given two saved accounts", func() {
|
||||
ac1cmd := m.CreateAccountCommand{Login: "ac1", Email: "ac1@test.com"}
|
||||
ac2cmd := m.CreateAccountCommand{Login: "ac2", Email: "ac2@test.com"}
|
||||
|
||||
err := CreateAccount(&ac1cmd)
|
||||
err = CreateAccount(&ac2cmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ac1 := ac1cmd.Result
|
||||
ac2 := ac2cmd.Result
|
||||
|
||||
Convey("Should be able to read account info projection", func() {
|
||||
query := m.GetAccountInfoQuery{Id: ac1.Id}
|
||||
err = GetAccountInfo(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.Email, ShouldEqual, "ac1@test.com")
|
||||
})
|
||||
|
||||
Convey("Can add collaborator", func() {
|
||||
cmd := m.AddCollaboratorCommand{
|
||||
AccountId: ac1.Id,
|
||||
CollaboratorId: ac2.Id,
|
||||
Role: m.ROLE_READ_WRITE,
|
||||
}
|
||||
|
||||
err := AddCollaborator(&cmd)
|
||||
Convey("Saved without error", func() {
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Collaborator should be included in account info projection", func() {
|
||||
query := m.GetAccountInfoQuery{Id: ac1.Id}
|
||||
err = GetAccountInfo(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result.Collaborators[0].CollaboratorId, ShouldEqual, ac2.Id)
|
||||
So(query.Result.Collaborators[0].Role, ShouldEqual, m.ROLE_READ_WRITE)
|
||||
So(query.Result.Collaborators[0].Email, ShouldEqual, "ac2@test.com")
|
||||
})
|
||||
|
||||
Convey("Can get other accounts", func() {
|
||||
query := m.GetOtherAccountsQuery{AccountId: ac2.Id}
|
||||
err := GetOtherAccounts(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result[0].Email, ShouldEqual, "ac1@test.com")
|
||||
})
|
||||
|
||||
Convey("Can set using account", func() {
|
||||
cmd := m.SetUsingAccountCommand{AccountId: ac2.Id, UsingAccountId: ac1.Id}
|
||||
err := SetUsingAccount(&cmd)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
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", SaveDashboard)
|
||||
bus.AddHandler("sql", GetDashboard)
|
||||
bus.AddHandler("sql", DeleteDashboard)
|
||||
bus.AddHandler("sql", SearchDashboards)
|
||||
bus.AddHandler("sql", GetDashboardTags)
|
||||
}
|
||||
|
||||
func SaveDashboard(cmd *m.SaveDashboardCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
dash := cmd.GetDashboardModel()
|
||||
|
||||
// try get existing dashboard
|
||||
existing := m.Dashboard{Slug: dash.Slug, AccountId: dash.AccountId}
|
||||
hasExisting, err := sess.Get(&existing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasExisting && dash.Id != existing.Id {
|
||||
return m.ErrDashboardWithSameNameExists
|
||||
}
|
||||
|
||||
if dash.Id == 0 {
|
||||
_, err = sess.Insert(dash)
|
||||
} else {
|
||||
_, err = sess.Id(dash.Id).Update(dash)
|
||||
}
|
||||
|
||||
// delete existing tabs
|
||||
_, err = sess.Exec("DELETE FROM dashboard_tag WHERE dashboard_id=?", dash.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// insert new tags
|
||||
tags := dash.GetTags()
|
||||
if len(tags) > 0 {
|
||||
tagRows := make([]DashboardTag, len(tags))
|
||||
for _, tag := range tags {
|
||||
tagRows = append(tagRows, DashboardTag{Term: tag, DashboardId: dash.Id})
|
||||
}
|
||||
sess.InsertMulti(&tagRows)
|
||||
}
|
||||
|
||||
cmd.Result = dash
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func GetDashboard(query *m.GetDashboardQuery) error {
|
||||
dashboard := m.Dashboard{Slug: query.Slug, AccountId: query.AccountId}
|
||||
has, err := x.Get(&dashboard)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
query.Result = &dashboard
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DashboardSearchProjection struct {
|
||||
Id int64
|
||||
Title string
|
||||
Slug string
|
||||
Term string
|
||||
}
|
||||
|
||||
func SearchDashboards(query *m.SearchDashboardsQuery) error {
|
||||
titleQuery := "%" + query.Title + "%"
|
||||
|
||||
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)
|
||||
|
||||
if len(query.Tag) > 0 {
|
||||
sess.And("dashboard_tag.term=?", query.Tag)
|
||||
}
|
||||
|
||||
var res []DashboardSearchProjection
|
||||
err := sess.Find(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = make([]*m.DashboardSearchHit, 0)
|
||||
hits := make(map[int64]*m.DashboardSearchHit)
|
||||
|
||||
for _, item := range res {
|
||||
hit, exists := hits[item.Id]
|
||||
if !exists {
|
||||
hit = &m.DashboardSearchHit{
|
||||
Title: item.Title,
|
||||
Slug: item.Slug,
|
||||
Tags: []string{},
|
||||
}
|
||||
query.Result = append(query.Result, hit)
|
||||
hits[item.Id] = hit
|
||||
}
|
||||
if len(item.Term) > 0 {
|
||||
hit.Tags = append(hit.Tags, item.Term)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
||||
sess := x.Sql("select count() as count, term from dashboard_tag group by term")
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
rawSql := "DELETE FROM Dashboard WHERE account_id=? and slug=?"
|
||||
_, err := sess.Exec(rawSql, cmd.AccountId, cmd.Slug)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
Convey("Should return dashboard model", func() {
|
||||
So(savedDash.Title, ShouldEqual, "test dash 23")
|
||||
So(savedDash.Slug, ShouldEqual, "test-dash-23")
|
||||
So(savedDash.Id, ShouldNotEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should be able to get dashboard", func() {
|
||||
query := m.GetDashboardQuery{
|
||||
Slug: "test-dash-23",
|
||||
AccountId: 1,
|
||||
}
|
||||
|
||||
err := GetDashboard(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(query.Result.Title, ShouldEqual, "test dash 23")
|
||||
So(query.Result.Slug, ShouldEqual, "test-dash-23")
|
||||
})
|
||||
|
||||
Convey("Should be able to search for dashboard", func() {
|
||||
query := m.SearchDashboardsQuery{
|
||||
Title: "%test%",
|
||||
AccountId: 1,
|
||||
}
|
||||
|
||||
err := SearchDashboards(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(query.Result), ShouldEqual, 1)
|
||||
hit := query.Result[0]
|
||||
So(len(hit.Tags), ShouldEqual, 2)
|
||||
})
|
||||
|
||||
Convey("Should be able to search for dashboards using tags", func() {
|
||||
query1 := m.SearchDashboardsQuery{Tag: "webapp", AccountId: 1}
|
||||
query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", AccountId: 1}
|
||||
|
||||
err := SearchDashboards(&query1)
|
||||
err = SearchDashboards(&query2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(query1.Result), ShouldEqual, 1)
|
||||
So(len(query2.Result), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Should not be able to save dashboard with same name", func() {
|
||||
cmd := m.SaveDashboardCommand{
|
||||
AccountId: 1,
|
||||
Dashboard: map[string]interface{}{
|
||||
"id": nil,
|
||||
"title": "test dash 23",
|
||||
"tags": []interface{}{},
|
||||
},
|
||||
}
|
||||
|
||||
err := SaveDashboard(&cmd)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Should be able to get dashboard tags", func() {
|
||||
query := m.GetDashboardTagsQuery{}
|
||||
|
||||
err := GetDashboardTags(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(query.Result), ShouldEqual, 3)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetDataSources)
|
||||
bus.AddHandler("sql", AddDataSource)
|
||||
bus.AddHandler("sql", DeleteDataSource)
|
||||
bus.AddHandler("sql", UpdateDataSource)
|
||||
bus.AddHandler("sql", GetDataSourceById)
|
||||
}
|
||||
|
||||
func GetDataSourceById(query *m.GetDataSourceByIdQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=? AND id=?", query.AccountId, query.Id)
|
||||
has, err := sess.Get(&query.Result)
|
||||
|
||||
if !has {
|
||||
return m.ErrDataSourceNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetDataSources(query *m.GetDataSourcesQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name")
|
||||
|
||||
query.Result = make([]*m.DataSource, 0)
|
||||
return sess.Find(&query.Result)
|
||||
}
|
||||
|
||||
func DeleteDataSource(cmd *m.DeleteDataSourceCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM data_source WHERE id=? and account_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func AddDataSource(cmd *m.AddDataSourceCommand) error {
|
||||
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
ds := &m.DataSource{
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
Access: cmd.Access,
|
||||
Url: cmd.Url,
|
||||
User: cmd.User,
|
||||
Password: cmd.Password,
|
||||
Database: cmd.Database,
|
||||
IsDefault: cmd.IsDefault,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(ds); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := updateIsDefaultFlag(ds, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Result = ds
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func updateIsDefaultFlag(ds *m.DataSource, sess *xorm.Session) error {
|
||||
// Handle is default flag
|
||||
if ds.IsDefault {
|
||||
rawSql := "UPDATE data_source SET is_default = 0 WHERE account_id=? AND id <> ?"
|
||||
if _, err := sess.Exec(rawSql, ds.AccountId, ds.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error {
|
||||
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
ds := &m.DataSource{
|
||||
Id: cmd.Id,
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Type: cmd.Type,
|
||||
Access: cmd.Access,
|
||||
Url: cmd.Url,
|
||||
User: cmd.User,
|
||||
Password: cmd.Password,
|
||||
Database: cmd.Database,
|
||||
Updated: time.Now(),
|
||||
IsDefault: cmd.IsDefault,
|
||||
}
|
||||
|
||||
sess.UseBool("is_default")
|
||||
|
||||
_, err := sess.Where("id=? and account_id=?", ds.Id, ds.AccountId).Update(ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateIsDefaultFlag(ds, sess)
|
||||
return err
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-xorm/xorm"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
)
|
||||
|
||||
func InitTestDB(t *testing.T) {
|
||||
x, err := xorm.NewEngine("sqlite3", ":memory:")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to init in memory sqllite3 db %v", err)
|
||||
}
|
||||
|
||||
SetEngine(x, false)
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
func TestDataAccess(t *testing.T) {
|
||||
|
||||
Convey("Testing DB", t, func() {
|
||||
InitTestDB(t)
|
||||
|
||||
Convey("Can add datasource", func() {
|
||||
|
||||
err := AddDataSource(&m.AddDataSourceCommand{
|
||||
AccountId: 10,
|
||||
Type: m.DS_INFLUXDB,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
Database: "site",
|
||||
})
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
query := m.GetDataSourcesQuery{AccountId: 10}
|
||||
err = GetDataSources(&query)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(query.Result), ShouldEqual, 1)
|
||||
|
||||
ds := query.Result[0]
|
||||
|
||||
So(ds.AccountId, ShouldEqual, 10)
|
||||
So(ds.Database, ShouldEqual, "site")
|
||||
})
|
||||
|
||||
Convey("Given a datasource", func() {
|
||||
|
||||
AddDataSource(&m.AddDataSourceCommand{
|
||||
AccountId: 10,
|
||||
Type: m.DS_GRAPHITE,
|
||||
Access: m.DS_ACCESS_DIRECT,
|
||||
Url: "http://test",
|
||||
})
|
||||
|
||||
query := m.GetDataSourcesQuery{AccountId: 10}
|
||||
GetDataSources(&query)
|
||||
ds := query.Result[0]
|
||||
|
||||
Convey("Can delete datasource", func() {
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: ds.AccountId})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
GetDataSources(&query)
|
||||
So(len(query.Result), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Can not delete datasource with wrong accountId", func() {
|
||||
err := DeleteDataSource(&m.DeleteDataSourceCommand{Id: ds.Id, AccountId: 123123})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
GetDataSources(&query)
|
||||
So(len(query.Result), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||
"github.com/torkelo/grafana-pro/pkg/log"
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/setting"
|
||||
"github.com/torkelo/grafana-pro/pkg/util"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/xorm"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []interface{}
|
||||
|
||||
HasEngine bool
|
||||
|
||||
DbCfg struct {
|
||||
Type, Host, Name, User, Pwd, Path, SslMode string
|
||||
}
|
||||
|
||||
UseSQLite3 bool
|
||||
)
|
||||
|
||||
type DashboardTag struct {
|
||||
Id int64
|
||||
DashboardId int64
|
||||
Term string
|
||||
}
|
||||
|
||||
func init() {
|
||||
tables = make([]interface{}, 0)
|
||||
|
||||
tables = append(tables, new(m.Account), new(m.Dashboard),
|
||||
new(m.Collaborator), new(m.DataSource), new(DashboardTag),
|
||||
new(m.Token))
|
||||
}
|
||||
|
||||
func EnsureAdminUser() {
|
||||
adminQuery := m.GetAccountByLoginQuery{Login: setting.AdminUser}
|
||||
|
||||
if err := bus.Dispatch(&adminQuery); err == m.ErrAccountNotFound {
|
||||
cmd := m.CreateAccountCommand{}
|
||||
cmd.Login = setting.AdminUser
|
||||
cmd.Email = setting.AdminUser + "@localhost"
|
||||
cmd.Salt = util.GetRandomString(10)
|
||||
cmd.Password = util.EncodePassword(setting.AdminPassword, cmd.Salt)
|
||||
cmd.IsAdmin = true
|
||||
|
||||
if err = bus.Dispatch(&cmd); err != nil {
|
||||
log.Fatal(3, "Failed to create default admin user", err)
|
||||
}
|
||||
|
||||
log.Info("Created default admin user: %v", setting.AdminUser)
|
||||
} else if err != nil {
|
||||
log.Fatal(3, "Could not determine if admin user exists: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func NewEngine() {
|
||||
x, err := getEngine()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(3, "Sqlstore: Fail to connect to database: %v", err)
|
||||
}
|
||||
|
||||
err = SetEngine(x, true)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(3, "fail to initialize orm engine: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func SetEngine(engine *xorm.Engine, enableLog bool) (err error) {
|
||||
x = engine
|
||||
|
||||
if err := x.Sync2(tables...); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %v\n", err)
|
||||
}
|
||||
|
||||
if enableLog {
|
||||
logPath := path.Join(setting.LogRootPath, "xorm.log")
|
||||
os.MkdirAll(path.Dir(logPath), os.ModePerm)
|
||||
|
||||
f, err := os.Create(logPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sqlstore.init(fail to create xorm.log): %v", err)
|
||||
}
|
||||
x.Logger = xorm.NewSimpleLogger(f)
|
||||
|
||||
x.ShowSQL = true
|
||||
x.ShowInfo = true
|
||||
x.ShowDebug = true
|
||||
x.ShowErr = true
|
||||
x.ShowWarn = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEngine() (*xorm.Engine, error) {
|
||||
LoadConfig()
|
||||
|
||||
cnnstr := ""
|
||||
switch DbCfg.Type {
|
||||
case "mysql":
|
||||
cnnstr = fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8",
|
||||
DbCfg.User, DbCfg.Pwd, DbCfg.Host, DbCfg.Name)
|
||||
case "postgres":
|
||||
var host, port = "127.0.0.1", "5432"
|
||||
fields := strings.Split(DbCfg.Host, ":")
|
||||
if len(fields) > 0 && len(strings.TrimSpace(fields[0])) > 0 {
|
||||
host = fields[0]
|
||||
}
|
||||
if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
|
||||
port = fields[1]
|
||||
}
|
||||
cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s",
|
||||
DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode)
|
||||
case "sqlite3":
|
||||
os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm)
|
||||
cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
|
||||
}
|
||||
|
||||
log.Info("Database: %v, ConnectionString: %v", DbCfg.Type, cnnstr)
|
||||
|
||||
return xorm.NewEngine(DbCfg.Type, cnnstr)
|
||||
}
|
||||
|
||||
func LoadConfig() {
|
||||
DbCfg.Type = setting.Cfg.MustValue("database", "type")
|
||||
if DbCfg.Type == "sqlite3" {
|
||||
UseSQLite3 = true
|
||||
}
|
||||
DbCfg.Host = setting.Cfg.MustValue("database", "host")
|
||||
DbCfg.Name = setting.Cfg.MustValue("database", "name")
|
||||
DbCfg.User = setting.Cfg.MustValue("database", "user")
|
||||
if len(DbCfg.Pwd) == 0 {
|
||||
DbCfg.Pwd = setting.Cfg.MustValue("database", "password")
|
||||
}
|
||||
DbCfg.SslMode = setting.Cfg.MustValue("database", "ssl_mode")
|
||||
DbCfg.Path = setting.Cfg.MustValue("database", "path", "data/grafana.db")
|
||||
}
|
||||
|
||||
type dbTransactionFunc func(sess *xorm.Session) error
|
||||
|
||||
func inTransaction(callback dbTransactionFunc) error {
|
||||
var err error
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
if err = sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = callback(sess)
|
||||
|
||||
if err != nil {
|
||||
sess.Rollback()
|
||||
return err
|
||||
} else if err = sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetTokens)
|
||||
bus.AddHandler("sql", AddToken)
|
||||
bus.AddHandler("sql", UpdateToken)
|
||||
bus.AddHandler("sql", DeleteToken)
|
||||
}
|
||||
|
||||
func GetTokens(query *m.GetTokensQuery) error {
|
||||
sess := x.Limit(100, 0).Where("account_id=?", query.AccountId).Asc("name")
|
||||
|
||||
query.Result = make([]*m.Token, 0)
|
||||
return sess.Find(&query.Result)
|
||||
}
|
||||
|
||||
func DeleteToken(cmd *m.DeleteTokenCommand) error {
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
var rawSql = "DELETE FROM token WHERE id=? and account_id=?"
|
||||
_, err := sess.Exec(rawSql, cmd.Id, cmd.AccountId)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func AddToken(cmd *m.AddTokenCommand) error {
|
||||
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
t := m.Token{
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Token: cmd.Token,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if _, err := sess.Insert(&t); err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Result = &t
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateToken(cmd *m.UpdateTokenCommand) error {
|
||||
|
||||
return inTransaction(func(sess *xorm.Session) error {
|
||||
t := m.Token{
|
||||
Id: cmd.Id,
|
||||
AccountId: cmd.AccountId,
|
||||
Name: cmd.Name,
|
||||
Role: cmd.Role,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
_, err := sess.Where("id=? and account_id=?", t.Id, t.AccountId).Update(&t)
|
||||
return err
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user