PluginManager: Make Plugins, Renderer and DataSources non-global (#31866)

* PluginManager: Make Plugins and DataSources non-global

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix integration tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Replace outdated command

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* DashboardService: Ensure it gets constructed with necessary parameters

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix build

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* DashboardService: Ensure it gets constructed with necessary parameters

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Remove dead code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Remove FocusConvey

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Remove dead code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Undo interface changes

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Backend: Move tsdbifaces.RequestHandler to plugins.DataRequestHandler

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Rename to DataSourceCount

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Consolidate dashboard interfaces into one

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix dashboard integration tests

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen
2021-03-17 16:06:10 +01:00
committed by GitHub
parent f1df32ac03
commit 87c3a2b790
95 changed files with 2455 additions and 2426 deletions
+29 -9
View File
@@ -2,6 +2,7 @@ package sqlstore
import (
"bytes"
"context"
"fmt"
"strings"
"time"
@@ -166,18 +167,18 @@ func deleteAlertDefinition(dashboardId int64, sess *DBSession) error {
return nil
}
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
return inTransaction(func(sess *DBSession) error {
existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
func (ss *SQLStore) SaveAlerts(dashID int64, alerts []*models.Alert) error {
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
if err != nil {
return err
}
if err := updateAlerts(existingAlerts, cmd, sess); err != nil {
if err := updateAlerts(existingAlerts, alerts, sess); err != nil {
return err
}
if err := deleteMissingAlerts(existingAlerts, cmd, sess); err != nil {
if err := deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
return err
}
@@ -185,8 +186,27 @@ func SaveAlerts(cmd *models.SaveAlertsCommand) error {
})
}
func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand, sess *DBSession) error {
for _, alert := range cmd.Alerts {
func SaveAlerts(cmd *models.SaveAlertsCommand) error {
return inTransaction(func(sess *DBSession) error {
existingAlerts, err := GetAlertsByDashboardId2(cmd.DashboardId, sess)
if err != nil {
return err
}
if err := updateAlerts(existingAlerts, cmd.Alerts, sess); err != nil {
return err
}
if err := deleteMissingAlerts(existingAlerts, cmd.Alerts, sess); err != nil {
return err
}
return nil
})
}
func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *DBSession) error {
for _, alert := range alerts {
update := false
var alertToUpdate *models.Alert
@@ -245,11 +265,11 @@ func updateAlerts(existingAlerts []*models.Alert, cmd *models.SaveAlertsCommand,
return nil
}
func deleteMissingAlerts(alerts []*models.Alert, cmd *models.SaveAlertsCommand, sess *DBSession) error {
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *DBSession) error {
for _, missingAlert := range alerts {
missing := true
for _, k := range cmd.Alerts {
for _, k := range existingAlerts {
if missingAlert.PanelId == k.PanelId {
missing = false
break
+9 -7
View File
@@ -30,10 +30,11 @@ func TestAlertingDataAccess(t *testing.T) {
defer resetTimeNow()
Convey("Testing Alerting data access", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
evalData, _ := simplejson.NewJson([]byte(`{"test": "test"}`))
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
So(err, ShouldBeNil)
items := []*models.Alert{
{
PanelId: 1,
@@ -54,7 +55,7 @@ func TestAlertingDataAccess(t *testing.T) {
UserId: 1,
}
err := SaveAlerts(&cmd)
err = SaveAlerts(&cmd)
Convey("Can create one alert", func() {
So(err, ShouldBeNil)
@@ -271,10 +272,11 @@ func TestPausingAlerts(t *testing.T) {
defer resetTimeNow()
Convey("Given an alert", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
testDash := insertTestDashboard(t, "dashboard with alerts", 1, 0, false, "alert")
alert, _ := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New())
So(err, ShouldBeNil)
stateDateBeforePause := alert.NewStateDate
stateDateAfterPause := stateDateBeforePause
+76 -44
View File
@@ -1,6 +1,8 @@
package sqlstore
import (
"context"
"fmt"
"strings"
"time"
@@ -24,7 +26,6 @@ var shadowSearchCounter = prometheus.NewCounterVec(
)
func init() {
bus.AddHandler("sql", SaveDashboard)
bus.AddHandler("sql", GetDashboard)
bus.AddHandler("sql", GetDashboards)
bus.AddHandler("sql", DeleteDashboard)
@@ -35,7 +36,6 @@ func init() {
bus.AddHandler("sql", GetDashboardsByPluginId)
bus.AddHandler("sql", GetDashboardPermissionsForUser)
bus.AddHandler("sql", GetDashboardsBySlug)
bus.AddHandler("sql", ValidateDashboardBeforeSave)
bus.AddHandler("sql", HasEditPermissionInFolders)
bus.AddHandler("sql", HasAdminPermissionInFolders)
@@ -44,10 +44,11 @@ func init() {
var generateNewUid func() string = util.GenerateShortUID
func SaveDashboard(cmd *models.SaveDashboardCommand) error {
return inTransaction(func(sess *DBSession) error {
return saveDashboard(sess, cmd)
func (ss *SQLStore) SaveDashboard(cmd models.SaveDashboardCommand) (*models.Dashboard, error) {
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
return saveDashboard(sess, &cmd)
})
return cmd.Result, err
}
func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
@@ -162,7 +163,7 @@ func saveDashboard(sess *DBSession, cmd *models.SaveDashboardCommand) error {
cmd.Result = dash
return err
return nil
}
func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
@@ -182,6 +183,26 @@ func generateNewDashboardUid(sess *DBSession, orgId int64) (string, error) {
return "", models.ErrDashboardFailedGenerateUniqueUid
}
// GetDashboard gets a dashboard.
func (ss *SQLStore) GetDashboard(id, orgID int64, uid, slug string) (*models.Dashboard, error) {
if id == 0 && slug == "" && uid == "" {
return nil, models.ErrDashboardIdentifierNotSet
}
dashboard := models.Dashboard{Slug: slug, OrgId: orgID, Id: id, Uid: uid}
has, err := ss.engine.Get(&dashboard)
if err != nil {
return nil, err
} else if !has {
return nil, models.ErrDashboardNotFound
}
dashboard.SetId(dashboard.Id)
dashboard.SetUid(dashboard.Uid)
return &dashboard, nil
}
// TODO: Remove me
func GetDashboard(query *models.GetDashboardQuery) error {
if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {
return models.ErrDashboardIdentifierNotSet
@@ -573,20 +594,20 @@ func GetDashboardUIDById(query *models.GetDashboardRefByIdQuery) error {
return nil
}
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
dash := cmd.Dashboard
func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, dash *models.Dashboard, overwrite bool) (bool, error) {
dashWithIdExists := false
isParentFolderChanged := false
var existingById models.Dashboard
if dash.Id > 0 {
var err error
dashWithIdExists, err = sess.Where("id=? AND org_id=?", dash.Id, dash.OrgId).Get(&existingById)
if err != nil {
return err
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by ID failed: %w", err)
}
if !dashWithIdExists {
return models.ErrDashboardNotFound
return isParentFolderChanged, models.ErrDashboardNotFound
}
if dash.Uid == "" {
@@ -598,30 +619,32 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
var existingByUid models.Dashboard
if dash.Uid != "" {
var err error
dashWithUidExists, err = sess.Where("org_id=? AND uid=?", dash.OrgId, dash.Uid).Get(&existingByUid)
if err != nil {
return err
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by UID failed: %w", err)
}
}
if dash.FolderId > 0 {
var existingFolder models.Dashboard
folderExists, folderErr := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId, dialect.BooleanStr(true)).Get(&existingFolder)
if folderErr != nil {
return folderErr
folderExists, err := sess.Where("org_id=? AND id=? AND is_folder=?", dash.OrgId, dash.FolderId,
dialect.BooleanStr(true)).Get(&existingFolder)
if err != nil {
return isParentFolderChanged, fmt.Errorf("SQL query for folder failed: %w", err)
}
if !folderExists {
return models.ErrDashboardFolderNotFound
return isParentFolderChanged, models.ErrDashboardFolderNotFound
}
}
if !dashWithIdExists && !dashWithUidExists {
return nil
return isParentFolderChanged, nil
}
if dashWithIdExists && dashWithUidExists && existingById.Id != existingByUid.Id {
return models.ErrDashboardWithSameUIDExists
return isParentFolderChanged, models.ErrDashboardWithSameUIDExists
}
existing := existingById
@@ -632,84 +655,92 @@ func getExistingDashboardByIdOrUidForUpdate(sess *DBSession, cmd *models.Validat
existing = existingByUid
if !dash.IsFolder {
cmd.Result.IsParentFolderChanged = true
isParentFolderChanged = true
}
}
if (existing.IsFolder && !dash.IsFolder) ||
(!existing.IsFolder && dash.IsFolder) {
return models.ErrDashboardTypeMismatch
return isParentFolderChanged, models.ErrDashboardTypeMismatch
}
if !dash.IsFolder && dash.FolderId != existing.FolderId {
cmd.Result.IsParentFolderChanged = true
isParentFolderChanged = true
}
// check for is someone else has written in between
if dash.Version != existing.Version {
if cmd.Overwrite {
if overwrite {
dash.SetVersion(existing.Version)
} else {
return models.ErrDashboardVersionMismatch
return isParentFolderChanged, models.ErrDashboardVersionMismatch
}
}
// do not allow plugin dashboard updates without overwrite flag
if existing.PluginId != "" && !cmd.Overwrite {
return models.UpdatePluginDashboardError{PluginId: existing.PluginId}
if existing.PluginId != "" && !overwrite {
return isParentFolderChanged, models.UpdatePluginDashboardError{PluginId: existing.PluginId}
}
return nil
return isParentFolderChanged, nil
}
func getExistingDashboardByTitleAndFolder(sess *DBSession, cmd *models.ValidateDashboardBeforeSaveCommand) error {
dash := cmd.Dashboard
func getExistingDashboardByTitleAndFolder(sess *DBSession, dash *models.Dashboard, overwrite,
isParentFolderChanged bool) (bool, error) {
var existing models.Dashboard
exists, err := sess.Where("org_id=? AND slug=? AND (is_folder=? OR folder_id=?)", dash.OrgId, dash.Slug, dialect.BooleanStr(true), dash.FolderId).Get(&existing)
exists, err := sess.Where("org_id=? AND slug=? AND (is_folder=? OR folder_id=?)", dash.OrgId, dash.Slug,
dialect.BooleanStr(true), dash.FolderId).Get(&existing)
if err != nil {
return err
return isParentFolderChanged, fmt.Errorf("SQL query for existing dashboard by org ID or folder ID failed: %w", err)
}
if exists && dash.Id != existing.Id {
if existing.IsFolder && !dash.IsFolder {
return models.ErrDashboardWithSameNameAsFolder
return isParentFolderChanged, models.ErrDashboardWithSameNameAsFolder
}
if !existing.IsFolder && dash.IsFolder {
return models.ErrDashboardFolderWithSameNameAsDashboard
return isParentFolderChanged, models.ErrDashboardFolderWithSameNameAsDashboard
}
if !dash.IsFolder && (dash.FolderId != existing.FolderId || dash.Id == 0) {
cmd.Result.IsParentFolderChanged = true
isParentFolderChanged = true
}
if cmd.Overwrite {
if overwrite {
dash.SetId(existing.Id)
dash.SetUid(existing.Uid)
dash.SetVersion(existing.Version)
} else {
return models.ErrDashboardWithSameNameInFolderExists
return isParentFolderChanged, models.ErrDashboardWithSameNameInFolderExists
}
}
return nil
return isParentFolderChanged, nil
}
func ValidateDashboardBeforeSave(cmd *models.ValidateDashboardBeforeSaveCommand) (err error) {
cmd.Result = &models.ValidateDashboardBeforeSaveResult{}
return inTransaction(func(sess *DBSession) error {
if err = getExistingDashboardByIdOrUidForUpdate(sess, cmd); err != nil {
func (ss *SQLStore) ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error) {
isParentFolderChanged := false
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
var err error
isParentFolderChanged, err = getExistingDashboardByIdOrUidForUpdate(sess, dashboard, overwrite)
if err != nil {
return err
}
if err = getExistingDashboardByTitleAndFolder(sess, cmd); err != nil {
isParentFolderChanged, err = getExistingDashboardByTitleAndFolder(sess, dashboard, overwrite,
isParentFolderChanged)
if err != nil {
return err
}
return nil
})
if err != nil {
return false, err
}
return isParentFolderChanged, nil
}
func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) error {
@@ -719,7 +750,8 @@ func HasEditPermissionInFolders(query *models.HasEditPermissionInFoldersQuery) e
}
builder := &SQLBuilder{}
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?", query.SignedInUser.OrgId, dialect.BooleanStr(true))
builder.Write("SELECT COUNT(dashboard.id) AS count FROM dashboard WHERE dashboard.org_id = ? AND dashboard.is_folder = ?",
query.SignedInUser.OrgId, dialect.BooleanStr(true))
builder.WriteDashboardPermissionFilter(query.SignedInUser, models.PERMISSION_EDIT)
type folderCount struct {
+9 -7
View File
@@ -1,24 +1,26 @@
package sqlstore
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
)
func init() {
bus.AddHandler("sql", UpdateDashboardAcl)
bus.AddHandler("sql", GetDashboardAclInfoList)
}
func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
return inTransaction(func(sess *DBSession) error {
func (ss *SQLStore) UpdateDashboardACL(dashboardID int64, items []*models.DashboardAcl) error {
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
// delete existing items
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", cmd.DashboardID)
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", dashboardID)
if err != nil {
return err
return fmt.Errorf("deleting from dashboard_acl failed: %w", err)
}
for _, item := range cmd.Items {
for _, item := range items {
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
return models.ErrDashboardAclInfoMissing
}
@@ -35,7 +37,7 @@ func UpdateDashboardAcl(cmd *models.UpdateDashboardAclCommand) error {
// Update dashboard HasAcl flag
dashboard := models.Dashboard{HasAcl: true}
_, err = sess.Cols("has_acl").Where("id=?", cmd.DashboardID).Update(&dashboard)
_, err = sess.Cols("has_acl").Where("id=?", dashboardID).Update(&dashboard)
return err
})
}
+19 -22
View File
@@ -11,14 +11,15 @@ import (
func TestDashboardAclDataAccess(t *testing.T) {
Convey("Testing DB", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given a dashboard folder and a user", func() {
currentUser := createUser(t, "viewer", "Viewer", false)
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
childDash := insertTestDashboard(t, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
childDash := insertTestDashboard(t, sqlStore, "2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
Convey("When adding dashboard permission with userId and teamId set to 0", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
OrgID: 1,
DashboardID: savedFolder.Id,
Permission: models.PERMISSION_EDIT,
@@ -61,10 +62,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Given dashboard folder with removed default permissions", func() {
err := UpdateDashboardAcl(&models.UpdateDashboardAclCommand{
DashboardID: savedFolder.Id,
Items: []*models.DashboardAcl{},
})
err := sqlStore.UpdateDashboardACL(savedFolder.Id, nil)
So(err, ShouldBeNil)
Convey("When reading dashboard acl should return no acl items", func() {
@@ -78,7 +76,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Given dashboard folder permission", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
OrgID: 1,
UserID: currentUser.Id,
DashboardID: savedFolder.Id,
@@ -97,7 +95,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Given child dashboard permission", func() {
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
OrgID: 1,
UserID: currentUser.Id,
DashboardID: childDash.Id,
@@ -121,7 +119,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Given child dashboard permission in folder with no permissions", func() {
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
OrgID: 1,
UserID: currentUser.Id,
DashboardID: childDash.Id,
@@ -149,7 +147,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Should be able to add dashboard permission", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
OrgID: 1,
UserID: currentUser.Id,
DashboardID: savedFolder.Id,
@@ -169,7 +167,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(q1.Result[0].UserEmail, ShouldEqual, currentUser.Email)
Convey("Should be able to delete an existing permission", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id)
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id)
So(err, ShouldBeNil)
q3 := &models.GetDashboardAclInfoListQuery{DashboardID: savedFolder.Id, OrgID: 1}
@@ -180,14 +178,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
Convey("Given a team", func() {
group1 := models.CreateTeamCommand{Name: "group1 name", OrgId: 1}
err := CreateTeam(&group1)
team1, err := sqlStore.CreateTeam("group1 name", "", 1)
So(err, ShouldBeNil)
Convey("Should be able to add a user permission for a team", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
OrgID: 1,
TeamID: group1.Result.Id,
TeamID: team1.Id,
DashboardID: savedFolder.Id,
Permission: models.PERMISSION_EDIT,
})
@@ -198,13 +195,13 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(err, ShouldBeNil)
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(q1.Result[0].Permission, ShouldEqual, models.PERMISSION_EDIT)
So(q1.Result[0].TeamId, ShouldEqual, group1.Result.Id)
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
})
Convey("Should be able to update an existing permission for a team", func() {
err := testHelperUpdateDashboardAcl(savedFolder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, savedFolder.Id, models.DashboardAcl{
OrgID: 1,
TeamID: group1.Result.Id,
TeamID: team1.Id,
DashboardID: savedFolder.Id,
Permission: models.PERMISSION_ADMIN,
})
@@ -216,7 +213,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(len(q3.Result), ShouldEqual, 1)
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(q3.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
So(q3.Result[0].TeamId, ShouldEqual, group1.Result.Id)
So(q3.Result[0].TeamId, ShouldEqual, team1.Id)
})
})
})
+59 -34
View File
@@ -7,21 +7,22 @@ import (
. "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
)
func TestDashboardFolderDataAccess(t *testing.T) {
Convey("Testing DB", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given one dashboard folder with two dashboards and one dashboard in the root folder", func() {
folder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
childDash := insertTestDashboard(t, "test dash 23", 1, folder.Id, false, "prod", "webapp")
insertTestDashboard(t, "test dash 45", 1, folder.Id, false, "prod")
folder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
childDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, folder.Id, false, "prod", "webapp")
insertTestDashboard(t, sqlStore, "test dash 45", 1, folder.Id, false, "prod")
currentUser := createUser(t, "viewer", "Viewer", false)
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
Convey("and no acls are set", func() {
Convey("should return all dashboards", func() {
@@ -40,7 +41,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and acl is set for dashboard folder", func() {
var otherUser int64 = 999
err := testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id, models.DashboardAcl{
DashboardID: folder.Id,
OrgID: 1,
UserID: otherUser,
@@ -61,7 +62,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("when the user is given permission", func() {
err := testHelperUpdateDashboardAcl(folder.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id, models.DashboardAcl{
DashboardID: folder.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
@@ -102,9 +103,9 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("and acl is set for dashboard child and folder has all permissions removed", func() {
var otherUser int64 = 999
err := testHelperUpdateDashboardAcl(folder.Id)
err := testHelperUpdateDashboardAcl(t, sqlStore, folder.Id)
So(err, ShouldBeNil)
err = testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{
err = testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
DashboardID: folder.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
@@ -120,7 +121,9 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("when the user is given permission to child", func() {
err := testHelperUpdateDashboardAcl(childDash.Id, models.DashboardAcl{DashboardID: childDash.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT})
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash.Id, models.DashboardAcl{
DashboardID: childDash.Id, OrgID: 1, UserID: currentUser.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("should be able to search for child dashboard but not folder", func() {
@@ -156,18 +159,24 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("Given two dashboard folders with one dashboard each and one dashboard in the root folder", func() {
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
dashInRoot := insertTestDashboard(t, "test dash 67", 1, 0, false, "prod")
childDash1 := insertTestDashboard(t, "child dash 1", 1, folder1.Id, false, "prod")
childDash2 := insertTestDashboard(t, "child dash 2", 1, folder2.Id, false, "prod")
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
dashInRoot := insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod")
childDash1 := insertTestDashboard(t, sqlStore, "child dash 1", 1, folder1.Id, false, "prod")
childDash2 := insertTestDashboard(t, sqlStore, "child dash 2", 1, folder2.Id, false, "prod")
currentUser := createUser(t, "viewer", "Viewer", false)
currentUser := createUser(t, sqlStore, "viewer", "Viewer", false)
var rootFolderId int64 = 0
Convey("and one folder is expanded, the other collapsed", func() {
Convey("should return dashboards in root and expanded folder", func() {
query := &search.FindPersistedDashboardsQuery{FolderIds: []int64{rootFolderId, folder1.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id, OrgId: 1, OrgRole: models.ROLE_VIEWER}, OrgId: 1}
query := &search.FindPersistedDashboardsQuery{
FolderIds: []int64{
rootFolderId, folder1.Id}, SignedInUser: &models.SignedInUser{UserId: currentUser.Id,
OrgId: 1, OrgRole: models.ROLE_VIEWER,
},
OrgId: 1,
}
err := SearchDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 4)
@@ -179,14 +188,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("and acl is set for one dashboard folder", func() {
var otherUser int64 = 999
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
const otherUser int64 = 999
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
DashboardID: folder1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("and a dashboard is moved from folder without acl to the folder with an acl", func() {
moveDashboard(1, childDash2.Data, folder1.Id)
moveDashboard(t, sqlStore, 1, childDash2.Data, folder1.Id)
Convey("should not return folder with acl or its children", func() {
query := &search.FindPersistedDashboardsQuery{
@@ -201,7 +210,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
})
Convey("and a dashboard is moved from folder with acl to the folder without an acl", func() {
moveDashboard(1, childDash1.Data, folder2.Id)
moveDashboard(t, sqlStore, 1, childDash1.Data, folder2.Id)
Convey("should return folder without acl and its children", func() {
query := &search.FindPersistedDashboardsQuery{
@@ -220,12 +229,12 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("and a dashboard with an acl is moved to the folder without an acl", func() {
err := testHelperUpdateDashboardAcl(childDash1.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, childDash1.Id, models.DashboardAcl{
DashboardID: childDash1.Id, OrgID: 1, UserID: otherUser, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
moveDashboard(1, childDash1.Data, folder2.Id)
moveDashboard(t, sqlStore, 1, childDash1.Data, folder2.Id)
Convey("should return folder without acl but not the dashboard with acl", func() {
query := &search.FindPersistedDashboardsQuery{
@@ -246,13 +255,13 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("Given two dashboard folders", func() {
folder1 := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod")
folder2 := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod")
insertTestDashboard(t, "folder in another org", 2, 0, true, "prod")
folder1 := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod")
folder2 := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod")
insertTestDashboard(t, sqlStore, "folder in another org", 2, 0, true, "prod")
adminUser := createUser(t, "admin", "Admin", true)
editorUser := createUser(t, "editor", "Editor", false)
viewerUser := createUser(t, "viewer", "Viewer", false)
adminUser := createUser(t, sqlStore, "admin", "Admin", true)
editorUser := createUser(t, sqlStore, "editor", "Editor", false)
viewerUser := createUser(t, sqlStore, "viewer", "Viewer", false)
Convey("Admin users", func() {
Convey("Should have write access to all dashboard folders in their org", func() {
@@ -343,7 +352,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("Should have write access to one dashboard folder if default role changed to view for one folder", func() {
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
DashboardID: folder1.Id, OrgID: 1, UserID: editorUser.Id, Permission: models.PERMISSION_VIEW,
})
So(err, ShouldBeNil)
@@ -407,7 +416,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("Should be able to get one dashboard folder if default role changed to edit for one folder", func() {
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
@@ -438,7 +447,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("and admin permission is given for user with org role viewer in one dashboard folder", func() {
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_ADMIN,
})
So(err, ShouldBeNil)
@@ -454,7 +463,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
Convey("and edit permission is given for user with org role viewer in one dashboard folder", func() {
err := testHelperUpdateDashboardAcl(folder1.Id, models.DashboardAcl{
err := testHelperUpdateDashboardAcl(t, sqlStore, folder1.Id, models.DashboardAcl{
DashboardID: folder1.Id, OrgID: 1, UserID: viewerUser.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
@@ -472,3 +481,19 @@ func TestDashboardFolderDataAccess(t *testing.T) {
})
})
}
func moveDashboard(t *testing.T, sqlStore *SQLStore, orgId int64, dashboard *simplejson.Json,
newFolderId int64) *models.Dashboard {
t.Helper()
cmd := models.SaveDashboardCommand{
OrgId: orgId,
FolderId: newFolderId,
Dashboard: dashboard,
Overwrite: true,
}
dash, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
return dash
}
+27 -31
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"errors"
"github.com/grafana/grafana/pkg/bus"
@@ -8,9 +9,6 @@ import (
)
func init() {
bus.AddHandler("sql", GetProvisionedDashboardDataQuery)
bus.AddHandler("sql", SaveProvisionedDashboard)
bus.AddHandler("sql", GetProvisionedDataByDashboardId)
bus.AddHandler("sql", UnprovisionDashboard)
bus.AddHandler("sql", DeleteOrphanedProvisionedDashboards)
}
@@ -22,64 +20,62 @@ type DashboardExtras struct {
Value string
}
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
result := &models.DashboardProvisioning{}
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
func (ss *SQLStore) GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) {
var data models.DashboardProvisioning
exists, err := x.Where("dashboard_id = ?", dashboardID).Get(&data)
if err != nil {
return err
return nil, err
}
if exist {
cmd.Result = result
if exists {
return &data, nil
}
return nil
return nil, nil
}
func SaveProvisionedDashboard(cmd *models.SaveProvisionedDashboardCommand) error {
return inTransaction(func(sess *DBSession) error {
err := saveDashboard(sess, cmd.DashboardCmd)
if err != nil {
func (ss *SQLStore) SaveProvisionedDashboard(cmd models.SaveDashboardCommand,
provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
if err := saveDashboard(sess, &cmd); err != nil {
return err
}
cmd.Result = cmd.DashboardCmd.Result
if cmd.DashboardProvisioning.Updated == 0 {
cmd.DashboardProvisioning.Updated = cmd.Result.Updated.Unix()
if provisioning.Updated == 0 {
provisioning.Updated = cmd.Result.Updated.Unix()
}
return saveProvisionedData(sess, cmd.DashboardProvisioning, cmd.Result)
return saveProvisionedData(sess, provisioning, cmd.Result)
})
return cmd.Result, err
}
func saveProvisionedData(sess *DBSession, cmd *models.DashboardProvisioning, dashboard *models.Dashboard) error {
func saveProvisionedData(sess *DBSession, provisioning *models.DashboardProvisioning, dashboard *models.Dashboard) error {
result := &models.DashboardProvisioning{}
exist, err := sess.Where("dashboard_id=? AND name = ?", dashboard.Id, cmd.Name).Get(result)
exist, err := sess.Where("dashboard_id=? AND name = ?", dashboard.Id, provisioning.Name).Get(result)
if err != nil {
return err
}
cmd.Id = result.Id
cmd.DashboardId = dashboard.Id
provisioning.Id = result.Id
provisioning.DashboardId = dashboard.Id
if exist {
_, err = sess.ID(result.Id).Update(cmd)
_, err = sess.ID(result.Id).Update(provisioning)
} else {
_, err = sess.Insert(cmd)
_, err = sess.Insert(provisioning)
}
return err
}
func GetProvisionedDashboardDataQuery(cmd *models.GetProvisionedDashboardDataQuery) error {
func (ss *SQLStore) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) {
var result []*models.DashboardProvisioning
if err := x.Where("name = ?", cmd.Name).Find(&result); err != nil {
return err
if err := ss.engine.Where("name = ?", name).Find(&result); err != nil {
return nil, err
}
cmd.Result = result
return nil
return result, nil
}
// UnprovisionDashboard removes row in dashboard_provisioning for the dashboard making it seem as if manually created.
@@ -13,9 +13,9 @@ import (
func TestDashboardProvisioningTest(t *testing.T) {
Convey("Testing Dashboard provisioning", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
folderCmd := &models.SaveDashboardCommand{
folderCmd := models.SaveDashboardCommand{
OrgId: 1,
FolderId: 0,
IsFolder: true,
@@ -25,13 +25,13 @@ func TestDashboardProvisioningTest(t *testing.T) {
}),
}
err := SaveDashboard(folderCmd)
dash, err := sqlStore.SaveDashboard(folderCmd)
So(err, ShouldBeNil)
saveDashboardCmd := &models.SaveDashboardCommand{
saveDashboardCmd := models.SaveDashboardCommand{
OrgId: 1,
IsFolder: false,
FolderId: folderCmd.Result.Id,
FolderId: dash.Id,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": "test dashboard",
@@ -41,43 +41,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
Convey("Saving dashboards with provisioning meta data", func() {
now := time.Now()
cmd := &models.SaveProvisionedDashboardCommand{
DashboardCmd: saveDashboardCmd,
DashboardProvisioning: &models.DashboardProvisioning{
Name: "default",
ExternalId: "/var/grafana.json",
Updated: now.Unix(),
},
provisioning := &models.DashboardProvisioning{
Name: "default",
ExternalId: "/var/grafana.json",
Updated: now.Unix(),
}
err := SaveProvisionedDashboard(cmd)
dash, err := sqlStore.SaveProvisionedDashboard(saveDashboardCmd, provisioning)
So(err, ShouldBeNil)
So(cmd.Result, ShouldNotBeNil)
So(cmd.Result.Id, ShouldNotEqual, 0)
dashId := cmd.Result.Id
So(dash, ShouldNotBeNil)
So(dash.Id, ShouldNotEqual, 0)
dashId := dash.Id
Convey("Deleting orphaned provisioned dashboards", func() {
anotherCmd := &models.SaveProvisionedDashboardCommand{
DashboardCmd: &models.SaveDashboardCommand{
OrgId: 1,
IsFolder: false,
FolderId: folderCmd.Result.Id,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": "another_dashboard",
}),
},
DashboardProvisioning: &models.DashboardProvisioning{
Name: "another_reader",
ExternalId: "/var/grafana.json",
Updated: now.Unix(),
},
saveCmd := models.SaveDashboardCommand{
OrgId: 1,
IsFolder: false,
FolderId: dash.Id,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": "another_dashboard",
}),
}
provisioning := &models.DashboardProvisioning{
Name: "another_reader",
ExternalId: "/var/grafana.json",
Updated: now.Unix(),
}
err := SaveProvisionedDashboard(anotherCmd)
anotherDash, err := sqlStore.SaveProvisionedDashboard(saveCmd, provisioning)
So(err, ShouldBeNil)
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherCmd.Result.Id}}
query := &models.GetDashboardsQuery{DashboardIds: []int64{anotherDash.Id}}
err = GetDashboards(query)
So(err, ShouldBeNil)
So(query.Result, ShouldNotBeNil)
@@ -85,7 +80,7 @@ func TestDashboardProvisioningTest(t *testing.T) {
deleteCmd := &models.DeleteOrphanedProvisionedDashboardsCommand{ReaderNames: []string{"default"}}
So(DeleteOrphanedProvisionedDashboards(deleteCmd), ShouldBeNil)
query = &models.GetDashboardsQuery{DashboardIds: []int64{cmd.Result.Id, anotherCmd.Result.Id}}
query = &models.GetDashboardsQuery{DashboardIds: []int64{dash.Id, anotherDash.Id}}
err = GetDashboards(query)
So(err, ShouldBeNil)
@@ -94,45 +89,38 @@ func TestDashboardProvisioningTest(t *testing.T) {
})
Convey("Can query for provisioned dashboards", func() {
query := &models.GetProvisionedDashboardDataQuery{Name: "default"}
err := GetProvisionedDashboardDataQuery(query)
rslt, err := sqlStore.GetProvisionedDashboardData("default")
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
So(query.Result[0].DashboardId, ShouldEqual, dashId)
So(query.Result[0].Updated, ShouldEqual, now.Unix())
So(len(rslt), ShouldEqual, 1)
So(rslt[0].DashboardId, ShouldEqual, dashId)
So(rslt[0].Updated, ShouldEqual, now.Unix())
})
Convey("Can query for one provisioned dashboard", func() {
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
err := GetProvisionedDataByDashboardId(query)
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
So(err, ShouldBeNil)
So(query.Result, ShouldNotBeNil)
So(data, ShouldNotBeNil)
})
Convey("Can query for none provisioned dashboard", func() {
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: 3000}
err := GetProvisionedDataByDashboardId(query)
data, err := sqlStore.GetProvisionedDataByDashboardID(3000)
So(err, ShouldBeNil)
So(query.Result, ShouldBeNil)
So(data, ShouldBeNil)
})
Convey("Deleting folder should delete provision meta data", func() {
deleteCmd := &models.DeleteDashboardCommand{
Id: folderCmd.Result.Id,
Id: dash.Id,
OrgId: 1,
}
So(DeleteDashboard(deleteCmd), ShouldBeNil)
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
err = GetProvisionedDataByDashboardId(query)
data, err := sqlStore.GetProvisionedDataByDashboardID(dash.Id)
So(err, ShouldBeNil)
So(query.Result, ShouldBeNil)
So(data, ShouldBeNil)
})
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
@@ -142,11 +130,9 @@ func TestDashboardProvisioningTest(t *testing.T) {
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashId}
err = GetProvisionedDataByDashboardId(query)
data, err := sqlStore.GetProvisionedDataByDashboardID(dashId)
So(err, ShouldBeNil)
So(query.Result, ShouldBeNil)
So(data, ShouldBeNil)
})
})
})
File diff suppressed because it is too large Load Diff
+42 -57
View File
@@ -22,13 +22,13 @@ import (
func TestDashboardDataAccess(t *testing.T) {
Convey("Testing DB", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given saved dashboard", func() {
savedFolder := insertTestDashboard(t, "1 test dash folder", 1, 0, true, "prod", "webapp")
savedDash := insertTestDashboard(t, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
insertTestDashboard(t, "test dash 45", 1, savedFolder.Id, false, "prod")
insertTestDashboard(t, "test dash 67", 1, 0, false, "prod", "webapp")
savedFolder := insertTestDashboard(t, sqlStore, "1 test dash folder", 1, 0, true, "prod", "webapp")
savedDash := insertTestDashboard(t, sqlStore, "test dash 23", 1, savedFolder.Id, false, "prod", "webapp")
insertTestDashboard(t, sqlStore, "test dash 45", 1, savedFolder.Id, false, "prod")
insertTestDashboard(t, sqlStore, "test dash 67", 1, 0, false, "prod", "webapp")
Convey("Should return dashboard model", func() {
So(savedDash.Title, ShouldEqual, "test dash 23")
@@ -104,7 +104,7 @@ func TestDashboardDataAccess(t *testing.T) {
})
Convey("Should be able to delete dashboard", func() {
dash := insertTestDashboard(t, "delete me", 1, 0, false, "delete this")
dash := insertTestDashboard(t, sqlStore, "delete me", 1, 0, false, "delete this")
err := DeleteDashboard(&models.DeleteDashboardCommand{
Id: dash.Id,
@@ -129,8 +129,7 @@ func TestDashboardDataAccess(t *testing.T) {
"tags": []interface{}{},
}),
}
err := SaveDashboard(&cmd)
_, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
generateNewUid = util.GenerateShortUID
@@ -145,13 +144,12 @@ func TestDashboardDataAccess(t *testing.T) {
}),
UserId: 100,
}
err := SaveDashboard(&cmd)
dashboard, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
So(cmd.Result.CreatedBy, ShouldEqual, 100)
So(cmd.Result.Created.IsZero(), ShouldBeFalse)
So(cmd.Result.UpdatedBy, ShouldEqual, 100)
So(cmd.Result.Updated.IsZero(), ShouldBeFalse)
So(dashboard.CreatedBy, ShouldEqual, 100)
So(dashboard.Created.IsZero(), ShouldBeFalse)
So(dashboard.UpdatedBy, ShouldEqual, 100)
So(dashboard.Updated.IsZero(), ShouldBeFalse)
})
Convey("Should be able to update dashboard by id and remove folderId", func() {
@@ -166,10 +164,9 @@ func TestDashboardDataAccess(t *testing.T) {
FolderId: 2,
UserId: 100,
}
err := SaveDashboard(&cmd)
dash, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
So(cmd.Result.FolderId, ShouldEqual, 2)
So(dash.FolderId, ShouldEqual, 2)
cmd = models.SaveDashboardCommand{
OrgId: 1,
@@ -182,8 +179,7 @@ func TestDashboardDataAccess(t *testing.T) {
Overwrite: true,
UserId: 100,
}
err = SaveDashboard(&cmd)
_, err = sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
query := models.GetDashboardQuery{
@@ -201,7 +197,7 @@ func TestDashboardDataAccess(t *testing.T) {
})
Convey("Should be able to delete empty folder", func() {
emptyFolder := insertTestDashboard(t, "2 test dash folder", 1, 0, true, "prod", "webapp")
emptyFolder := insertTestDashboard(t, sqlStore, "2 test dash folder", 1, 0, true, "prod", "webapp")
deleteCmd := &models.DeleteDashboardCommand{Id: emptyFolder.Id}
err := DeleteDashboard(deleteCmd)
@@ -236,7 +232,7 @@ func TestDashboardDataAccess(t *testing.T) {
}),
}
err := SaveDashboard(&cmd)
_, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldEqual, models.ErrDashboardNotFound)
})
@@ -250,8 +246,7 @@ func TestDashboardDataAccess(t *testing.T) {
"tags": []interface{}{},
}),
}
err := SaveDashboard(&cmd)
_, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
})
@@ -366,7 +361,7 @@ func TestDashboardDataAccess(t *testing.T) {
})
Convey("Given two dashboards, one is starred dashboard by user 10, other starred by user 1", func() {
starredDash := insertTestDashboard(t, "starred dash", 1, 0, false)
starredDash := insertTestDashboard(t, sqlStore, "starred dash", 1, 0, false)
err := StarDashboard(&models.StarDashboardCommand{
DashboardId: starredDash.Id,
UserId: 10,
@@ -396,9 +391,9 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Given a plugin with imported dashboards", func() {
pluginId := "test-app"
appFolder := insertTestDashboardForPlugin("app-test", 1, 0, true, pluginId)
insertTestDashboardForPlugin("app-dash1", 1, appFolder.Id, false, pluginId)
insertTestDashboardForPlugin("app-dash2", 1, appFolder.Id, false, pluginId)
appFolder := insertTestDashboardForPlugin(t, sqlStore, "app-test", 1, 0, true, pluginId)
insertTestDashboardForPlugin(t, sqlStore, "app-dash1", 1, appFolder.Id, false, pluginId)
insertTestDashboardForPlugin(t, sqlStore, "app-dash2", 1, appFolder.Id, false, pluginId)
Convey("Should return imported dashboard", func() {
query := models.GetDashboardsByPluginIdQuery{
@@ -417,9 +412,9 @@ func TestDashboardDataAccess(t *testing.T) {
func TestDashboard_SortingOptions(t *testing.T) {
// insertTestDashboard uses GoConvey's assertions. Workaround.
Convey("test with multiple sorting options", t, func() {
InitTestDB(t)
dashB := insertTestDashboard(t, "Beta", 1, 0, false)
dashA := insertTestDashboard(t, "Alfa", 1, 0, false)
sqlStore := InitTestDB(t)
dashB := insertTestDashboard(t, sqlStore, "Beta", 1, 0, false)
dashA := insertTestDashboard(t, sqlStore, "Alfa", 1, 0, false)
assert.NotZero(t, dashA.Id)
assert.Less(t, dashB.Id, dashA.Id)
@@ -441,7 +436,8 @@ func TestDashboard_SortingOptions(t *testing.T) {
})
}
func insertTestDashboard(t *testing.T, title string, orgId int64, folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
func insertTestDashboard(t *testing.T, sqlStore *SQLStore, title string, orgId int64,
folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
t.Helper()
cmd := models.SaveDashboardCommand{
@@ -454,17 +450,20 @@ func insertTestDashboard(t *testing.T, title string, orgId int64, folderId int64
"tags": tags,
}),
}
err := SaveDashboard(&cmd)
dash, err := sqlStore.SaveDashboard(cmd)
require.NoError(t, err)
require.NotNil(t, dash)
cmd.Result.Data.Set("id", cmd.Result.Id)
cmd.Result.Data.Set("uid", cmd.Result.Uid)
dash.Data.Set("id", dash.Id)
dash.Data.Set("uid", dash.Uid)
return cmd.Result
return dash
}
func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isFolder bool, pluginId string) *models.Dashboard {
func insertTestDashboardForPlugin(t *testing.T, sqlStore *SQLStore, title string, orgId int64,
folderId int64, isFolder bool, pluginId string) *models.Dashboard {
t.Helper()
cmd := models.SaveDashboardCommand{
OrgId: orgId,
FolderId: folderId,
@@ -476,13 +475,13 @@ func insertTestDashboardForPlugin(title string, orgId int64, folderId int64, isF
PluginId: pluginId,
}
err := SaveDashboard(&cmd)
dash, err := sqlStore.SaveDashboard(cmd)
So(err, ShouldBeNil)
return cmd.Result
return dash
}
func createUser(t *testing.T, name string, role string, isAdmin bool) models.User {
func createUser(t *testing.T, sqlStore *SQLStore, name string, role string, isAdmin bool) models.User {
t.Helper()
setting.AutoAssignOrg = true
@@ -490,27 +489,13 @@ func createUser(t *testing.T, name string, role string, isAdmin bool) models.Use
setting.AutoAssignOrgRole = role
currentUserCmd := models.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
err := CreateUser(context.Background(), &currentUserCmd)
currentUser, err := sqlStore.CreateUser(context.Background(), currentUserCmd)
require.NoError(t, err)
q1 := models.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
q1 := models.GetUserOrgListQuery{UserId: currentUser.Id}
err = GetUserOrgList(&q1)
require.NoError(t, err)
require.Equal(t, models.RoleType(role), q1.Result[0].Role)
return currentUserCmd.Result
}
func moveDashboard(orgId int64, dashboard *simplejson.Json, newFolderId int64) *models.Dashboard {
cmd := models.SaveDashboardCommand{
OrgId: orgId,
FolderId: newFolderId,
Dashboard: dashboard,
Overwrite: true,
}
err := SaveDashboard(&cmd)
So(err, ShouldBeNil)
return cmd.Result
return *currentUser
}
+13 -12
View File
@@ -13,7 +13,9 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
func updateTestDashboard(dashboard *models.Dashboard, data map[string]interface{}) {
func updateTestDashboard(t *testing.T, sqlStore *SQLStore, dashboard *models.Dashboard, data map[string]interface{}) {
t.Helper()
data["id"] = dashboard.Id
saveCmd := models.SaveDashboardCommand{
@@ -21,17 +23,16 @@ func updateTestDashboard(dashboard *models.Dashboard, data map[string]interface{
Overwrite: true,
Dashboard: simplejson.NewFromAny(data),
}
err := SaveDashboard(&saveCmd)
_, err := sqlStore.SaveDashboard(saveCmd)
So(err, ShouldBeNil)
}
func TestGetDashboardVersion(t *testing.T) {
Convey("Testing dashboard version retrieval", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Get a Dashboard ID and version ID", func() {
savedDash := insertTestDashboard(t, "test dash 26", 1, 0, false, "diff")
savedDash := insertTestDashboard(t, sqlStore, "test dash 26", 1, 0, false, "diff")
query := models.GetDashboardVersionQuery{
DashboardId: savedDash.Id,
@@ -71,8 +72,8 @@ func TestGetDashboardVersion(t *testing.T) {
func TestGetDashboardVersions(t *testing.T) {
Convey("Testing dashboard versions retrieval", t, func() {
InitTestDB(t)
savedDash := insertTestDashboard(t, "test dash 43", 1, 0, false, "diff-all")
sqlStore := InitTestDB(t)
savedDash := insertTestDashboard(t, sqlStore, "test dash 43", 1, 0, false, "diff-all")
Convey("Get all versions for a given Dashboard ID", func() {
query := models.GetDashboardVersionsQuery{DashboardId: savedDash.Id, OrgId: 1}
@@ -92,7 +93,7 @@ func TestGetDashboardVersions(t *testing.T) {
})
Convey("Get all versions for an updated dashboard", func() {
updateTestDashboard(savedDash, map[string]interface{}{
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
"tags": "different-tag",
})
@@ -107,14 +108,14 @@ func TestGetDashboardVersions(t *testing.T) {
func TestDeleteExpiredVersions(t *testing.T) {
Convey("Testing dashboard versions clean up", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
versionsToKeep := 5
versionsToWrite := 10
setting.DashboardVersionsToKeep = versionsToKeep
savedDash := insertTestDashboard(t, "test dash 53", 1, 0, false, "diff-all")
savedDash := insertTestDashboard(t, sqlStore, "test dash 53", 1, 0, false, "diff-all")
for i := 0; i < versionsToWrite-1; i++ {
updateTestDashboard(savedDash, map[string]interface{}{
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
"tags": "different-tag",
})
}
@@ -152,7 +153,7 @@ func TestDeleteExpiredVersions(t *testing.T) {
versionsToWriteBigNumber := perBatch*maxBatches + versionsToWrite
for i := 0; i < versionsToWriteBigNumber-versionsToWrite; i++ {
updateTestDashboard(savedDash, map[string]interface{}{
updateTestDashboard(t, sqlStore, savedDash, map[string]interface{}{
"tags": "different-tag",
})
}
+25 -22
View File
@@ -15,7 +15,7 @@ import (
func TestAccountDataAccess(t *testing.T) {
Convey("Testing Account DB Access", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given we have organizations, we can query them by IDs", func() {
var err error
@@ -78,13 +78,13 @@ func TestAccountDataAccess(t *testing.T) {
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
err := CreateUser(context.Background(), &ac1cmd)
ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
So(err, ShouldBeNil)
err = CreateUser(context.Background(), &ac2cmd)
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
So(err, ShouldBeNil)
q1 := models.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
q2 := models.GetUserOrgListQuery{UserId: ac2cmd.Result.Id}
q1 := models.GetUserOrgListQuery{UserId: ac1.Id}
q2 := models.GetUserOrgListQuery{UserId: ac2.Id}
err = GetUserOrgList(&q1)
So(err, ShouldBeNil)
err = GetUserOrgList(&q2)
@@ -101,13 +101,10 @@ func TestAccountDataAccess(t *testing.T) {
ac1cmd := models.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
ac2cmd := models.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
err := CreateUser(context.Background(), &ac1cmd)
err = CreateUser(context.Background(), &ac2cmd)
ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
So(err, ShouldBeNil)
ac1 := ac1cmd.Result
ac2 := ac2cmd.Result
Convey("Should be able to read user info projection", func() {
query := models.GetUserProfileQuery{UserId: ac1.Id}
err = GetUserProfile(&query)
@@ -266,9 +263,8 @@ func TestAccountDataAccess(t *testing.T) {
Convey("Given an org user with dashboard permissions", func() {
ac3cmd := models.CreateUserCommand{Login: "ac3", Email: "ac3@test.com", Name: "ac3 name", IsAdmin: false}
err := CreateUser(context.Background(), &ac3cmd)
ac3, err := sqlStore.CreateUser(context.Background(), ac3cmd)
So(err, ShouldBeNil)
ac3 := ac3cmd.Result
orgUserCmd := models.AddOrgUserCommand{
OrgId: ac1.OrgId,
@@ -284,13 +280,17 @@ func TestAccountDataAccess(t *testing.T) {
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 3)
dash1 := insertTestDashboard(t, "1 test dash", ac1.OrgId, 0, false, "prod", "webapp")
dash2 := insertTestDashboard(t, "2 test dash", ac3.OrgId, 0, false, "prod", "webapp")
dash1 := insertTestDashboard(t, sqlStore, "1 test dash", ac1.OrgId, 0, false, "prod", "webapp")
dash2 := insertTestDashboard(t, sqlStore, "2 test dash", ac3.OrgId, 0, false, "prod", "webapp")
err = testHelperUpdateDashboardAcl(dash1.Id, models.DashboardAcl{DashboardID: dash1.Id, OrgID: ac1.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT})
err = testHelperUpdateDashboardAcl(t, sqlStore, dash1.Id, models.DashboardAcl{
DashboardID: dash1.Id, OrgID: ac1.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
err = testHelperUpdateDashboardAcl(dash2.Id, models.DashboardAcl{DashboardID: dash2.Id, OrgID: ac3.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT})
err = testHelperUpdateDashboardAcl(t, sqlStore, dash2.Id, models.DashboardAcl{
DashboardID: dash2.Id, OrgID: ac3.OrgId, UserID: ac3.Id, Permission: models.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("When org user is deleted", func() {
@@ -322,13 +322,16 @@ func TestAccountDataAccess(t *testing.T) {
})
}
func testHelperUpdateDashboardAcl(dashboardId int64, items ...models.DashboardAcl) error {
cmd := models.UpdateDashboardAclCommand{DashboardID: dashboardId}
for _, i := range items {
item := i
func testHelperUpdateDashboardAcl(t *testing.T, sqlStore *SQLStore, dashboardID int64,
items ...models.DashboardAcl) error {
t.Helper()
var itemPtrs []*models.DashboardAcl
for _, it := range items {
item := it
item.Created = time.Now()
item.Updated = time.Now()
cmd.Items = append(cmd.Items, &item)
itemPtrs = append(itemPtrs, &item)
}
return UpdateDashboardAcl(&cmd)
return sqlStore.UpdateDashboardACL(dashboardID, itemPtrs)
}
+8 -6
View File
@@ -10,25 +10,27 @@ import (
)
func init() {
bus.AddHandler("sql", GetPluginSettings)
bus.AddHandler("sql", GetPluginSettingById)
bus.AddHandler("sql", UpdatePluginSetting)
bus.AddHandler("sql", UpdatePluginSettingVersion)
}
func GetPluginSettings(query *models.GetPluginSettingsQuery) error {
func (ss *SQLStore) GetPluginSettings(orgID int64) ([]*models.PluginSettingInfoDTO, error) {
sql := `SELECT org_id, plugin_id, enabled, pinned, plugin_version
FROM plugin_setting `
params := make([]interface{}, 0)
if query.OrgId != 0 {
if orgID != 0 {
sql += "WHERE org_id=?"
params = append(params, query.OrgId)
params = append(params, orgID)
}
sess := x.SQL(sql, params...)
query.Result = make([]*models.PluginSettingInfoDTO, 0)
return sess.Find(&query.Result)
var rslt []*models.PluginSettingInfoDTO
if err := sess.Find(&rslt); err != nil {
return nil, err
}
return rslt, nil
}
func GetPluginSettingById(query *models.GetPluginSettingByIdQuery) error {
@@ -5,7 +5,6 @@ package searchstore_test
import (
"context"
"fmt"
"testing"
"time"
@@ -34,13 +33,11 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
}
db := setupTestEnvironment(t)
err := createDashboards(0, 1, user.OrgId)
require.NoError(t, err)
createDashboards(t, db, 0, 1, user.OrgId)
// create one dashboard in another organization that shouldn't
// be listed in the results.
err = createDashboards(1, 2, 2)
require.NoError(t, err)
createDashboards(t, db, 1, 2, 2)
builder := &searchstore.Builder{
Filters: []interface{}{
@@ -51,7 +48,7 @@ func TestBuilder_EqualResults_Basic(t *testing.T) {
}
res := []sqlstore.DashboardSearchProjection{}
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(limit, page)
return sess.SQL(sql, params...).Find(&res)
})
@@ -77,8 +74,7 @@ func TestBuilder_Pagination(t *testing.T) {
}
db := setupTestEnvironment(t)
err := createDashboards(0, 25, user.OrgId)
require.NoError(t, err)
createDashboards(t, db, 0, 25, user.OrgId)
builder := &searchstore.Builder{
Filters: []interface{}{
@@ -91,7 +87,7 @@ func TestBuilder_Pagination(t *testing.T) {
resPg1 := []sqlstore.DashboardSearchProjection{}
resPg2 := []sqlstore.DashboardSearchProjection{}
resPg3 := []sqlstore.DashboardSearchProjection{}
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(15, 1)
err := sess.SQL(sql, params...).Find(&resPg1)
if err != nil {
@@ -124,8 +120,7 @@ func TestBuilder_Permissions(t *testing.T) {
}
db := setupTestEnvironment(t)
err := createDashboards(0, 1, user.OrgId)
require.NoError(t, err)
createDashboards(t, db, 0, 1, user.OrgId)
level := models.PERMISSION_EDIT
@@ -145,7 +140,7 @@ func TestBuilder_Permissions(t *testing.T) {
}
res := []sqlstore.DashboardSearchProjection{}
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
sql, params := builder.ToSQL(limit, page)
return sess.SQL(sql, params...).Find(&res)
})
@@ -161,10 +156,10 @@ func setupTestEnvironment(t *testing.T) *sqlstore.SQLStore {
return store
}
func createDashboards(startID, endID int, orgID int64) error {
if endID < startID {
return fmt.Errorf("startID must be smaller than endID")
}
func createDashboards(t *testing.T, db *sqlstore.SQLStore, startID, endID int, orgID int64) {
t.Helper()
require.GreaterOrEqual(t, endID, startID)
for i := startID; i < endID; i++ {
dashboard, err := simplejson.NewJson([]byte(`{
@@ -176,20 +171,15 @@ func createDashboards(startID, endID int, orgID int64) error {
"schemaVersion": 16,
"version": 0
}`))
if err != nil {
return err
}
err = sqlstore.SaveDashboard(&models.SaveDashboardCommand{
require.NoError(t, err)
_, err = db.SaveDashboard(models.SaveDashboardCommand{
Dashboard: dashboard,
UserId: 1,
OrgId: orgID,
UpdatedAt: time.Now(),
})
if err != nil {
return err
}
require.NoError(t, err)
}
return nil
}
// lexiCounter counts in a lexicographically sortable order.
+66 -85
View File
@@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSQLBuilder(t *testing.T) {
@@ -138,8 +139,8 @@ func TestSQLBuilder(t *testing.T) {
})
}
var shouldFind = true
var shouldNotFind = false
const shouldFind = true
const shouldNotFind = false
type DashboardProps struct {
OrgId int64
@@ -164,38 +165,35 @@ type dashboardResponse struct {
}
func test(t *testing.T, dashboardProps DashboardProps, dashboardPermission *DashboardPermission, search Search, shouldFind bool) {
// Will also cleanup the db
sqlStore := InitTestDB(t)
t.Helper()
dashboard, err := createDummyDashboard(dashboardProps)
if !assert.Equal(t, nil, err) {
return
}
t.Run("", func(t *testing.T) {
// Will also cleanup the db
sqlStore := InitTestDB(t)
var aclUserId int64
if dashboardPermission != nil {
aclUserId, err = createDummyAcl(dashboardPermission, search, dashboard.Id)
if !assert.Equal(t, nil, err) {
return
dashboard := createDummyDashboard(t, sqlStore, dashboardProps)
var aclUserID int64
if dashboardPermission != nil {
aclUserID = createDummyACL(t, sqlStore, dashboardPermission, search, dashboard.Id)
t.Logf("Created ACL with user ID %d\n", aclUserID)
}
}
dashboards, err := getDashboards(sqlStore, search, aclUserId)
if !assert.Equal(t, nil, err) {
return
}
dashboards := getDashboards(t, sqlStore, search, aclUserID)
if shouldFind {
if assert.Equal(t, 1, len(dashboards), "Should return one dashboard") {
assert.Equal(t, dashboards[0].Id, dashboard.Id, "Should return created dashboard")
if shouldFind {
require.Len(t, dashboards, 1, "Should return one dashboard")
assert.Equal(t, dashboard.Id, dashboards[0].Id, "Should return created dashboard")
} else {
assert.Empty(t, dashboards, "Should not return any dashboard")
}
} else {
assert.Equal(t, 0, len(dashboards), "Should node return any dashboard")
}
})
}
func createDummyUser() (*models.User, error) {
func createDummyUser(t *testing.T, sqlStore *SQLStore) *models.User {
t.Helper()
uid := strconv.Itoa(rand.Intn(9999999))
createUserCmd := &models.CreateUserCommand{
createUserCmd := models.CreateUserCommand{
Email: uid + "@example.com",
Login: uid,
Name: uid,
@@ -207,33 +205,28 @@ func createDummyUser() (*models.User, error) {
SkipOrgSetup: false,
DefaultOrgRole: string(models.ROLE_VIEWER),
}
err := CreateUser(context.Background(), createUserCmd)
if err != nil {
return nil, err
}
user, err := sqlStore.CreateUser(context.Background(), createUserCmd)
require.NoError(t, err)
return &createUserCmd.Result, nil
return user
}
func createDummyTeam() (*models.Team, error) {
cmd := &models.CreateTeamCommand{
// Does not matter in this tests actually
OrgId: 1,
Name: "test",
Email: "test@example.com",
}
err := CreateTeam(cmd)
if err != nil {
return nil, err
}
func createDummyTeam(t *testing.T, sqlStore *SQLStore) models.Team {
t.Helper()
return &cmd.Result, nil
team, err := sqlStore.CreateTeam("test", "test@example.com", 1)
require.NoError(t, err)
return team
}
func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, error) {
json, _ := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
func createDummyDashboard(t *testing.T, sqlStore *SQLStore, dashboardProps DashboardProps) *models.Dashboard {
t.Helper()
saveDashboardCmd := &models.SaveDashboardCommand{
json, err := simplejson.NewJson([]byte(`{"schemaVersion":17,"title":"gdev dashboards","uid":"","version":1}`))
require.NoError(t, err)
saveDashboardCmd := models.SaveDashboardCommand{
Dashboard: json,
UserId: 0,
Overwrite: false,
@@ -250,53 +243,40 @@ func createDummyDashboard(dashboardProps DashboardProps) (*models.Dashboard, err
saveDashboardCmd.OrgId = 1
}
err := SaveDashboard(saveDashboardCmd)
if err != nil {
return nil, err
}
dash, err := sqlStore.SaveDashboard(saveDashboardCmd)
require.NoError(t, err)
return saveDashboardCmd.Result, nil
t.Logf("Created dashboard with ID %d and org ID %d\n", dash.Id, dash.OrgId)
return dash
}
func createDummyAcl(dashboardPermission *DashboardPermission, search Search, dashboardId int64) (int64, error) {
func createDummyACL(t *testing.T, sqlStore *SQLStore, dashboardPermission *DashboardPermission, search Search, dashboardID int64) int64 {
t.Helper()
acl := &models.DashboardAcl{
OrgID: 1,
Created: time.Now(),
Updated: time.Now(),
Permission: dashboardPermission.Permission,
DashboardID: dashboardId,
DashboardID: dashboardID,
}
var user *models.User
var err error
if dashboardPermission.User {
user, err = createDummyUser()
if err != nil {
return 0, err
}
t.Logf("Creating user")
user = createDummyUser(t, sqlStore)
acl.UserID = user.Id
}
if dashboardPermission.Team {
team, err := createDummyTeam()
if err != nil {
return 0, err
}
t.Logf("Creating team")
team := createDummyTeam(t, sqlStore)
if search.UserFromACL {
user, err = createDummyUser()
if err != nil {
return 0, err
}
addTeamMemberCmd := &models.AddTeamMemberCommand{
UserId: user.Id,
OrgId: 1,
TeamId: team.Id,
}
err = AddTeamMember(addTeamMemberCmd)
if err != nil {
return 0, err
}
user = createDummyUser(t, sqlStore)
err := sqlStore.AddTeamMember(user.Id, 1, team.Id, false, 0)
require.NoError(t, err)
t.Logf("Created team member with ID %d", user.Id)
}
acl.TeamID = team.Id
@@ -306,18 +286,17 @@ func createDummyAcl(dashboardPermission *DashboardPermission, search Search, das
acl.Role = &dashboardPermission.Role
}
updateAclCmd := &models.UpdateDashboardAclCommand{
DashboardID: dashboardId,
Items: []*models.DashboardAcl{acl},
}
err = UpdateDashboardAcl(updateAclCmd)
err := sqlStore.UpdateDashboardACL(dashboardID, []*models.DashboardAcl{acl})
require.NoError(t, err)
if user != nil {
return user.Id, err
return user.Id
}
return 0, err
return 0
}
func getDashboards(sqlStore *SQLStore, search Search, aclUserId int64) ([]*dashboardResponse, error) {
func getDashboards(t *testing.T, sqlStore *SQLStore, search Search, aclUserID int64) []*dashboardResponse {
t.Helper()
builder := &SQLBuilder{}
signedInUser := &models.SignedInUser{
UserId: 9999999999,
@@ -335,12 +314,14 @@ func getDashboards(sqlStore *SQLStore, search Search, aclUserId int64) ([]*dashb
signedInUser.OrgRole = models.ROLE_VIEWER
}
if search.UserFromACL {
signedInUser.UserId = aclUserId
signedInUser.UserId = aclUserID
}
var res []*dashboardResponse
builder.Write("SELECT * FROM dashboard WHERE true")
builder.WriteDashboardPermissionFilter(signedInUser, search.RequiredPermission)
t.Logf("Searching for dashboards, SQL: %q\n", builder.GetSQLString())
err := sqlStore.engine.SQL(builder.GetSQLString(), builder.params...).Find(&res)
return res, err
require.NoError(t, err)
return res
}
-1
View File
@@ -22,7 +22,6 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/grafana/grafana/pkg/services/sqlstore/sqlutil"
"github.com/grafana/grafana/pkg/setting"
_ "github.com/grafana/grafana/pkg/tsdb/mssql"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
_ "github.com/lib/pq"
+8 -6
View File
@@ -13,8 +13,8 @@ import (
)
func TestStatsDataAccess(t *testing.T) {
InitTestDB(t)
populateDB(t)
sqlStore := InitTestDB(t)
populateDB(t, sqlStore)
t.Run("Get system stats should not results in error", func(t *testing.T) {
query := models.GetSystemStatsQuery{}
@@ -57,18 +57,20 @@ func TestStatsDataAccess(t *testing.T) {
})
}
func populateDB(t *testing.T) {
func populateDB(t *testing.T, sqlStore *SQLStore) {
t.Helper()
users := make([]models.User, 3)
for i := range users {
cmd := &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: fmt.Sprintf("usertest%v@test.com", i),
Name: fmt.Sprintf("user name %v", i),
Login: fmt.Sprintf("user_test_%v_login", i),
OrgName: fmt.Sprintf("Org #%v", i),
}
err := CreateUser(context.Background(), cmd)
user, err := sqlStore.CreateUser(context.Background(), cmd)
require.NoError(t, err)
users[i] = cmd.Result
users[i] = *user
}
// get 1st user's organisation
+25 -29
View File
@@ -2,6 +2,7 @@ package sqlstore
import (
"bytes"
"context"
"fmt"
"strings"
"time"
@@ -11,14 +12,12 @@ import (
)
func init() {
bus.AddHandler("sql", CreateTeam)
bus.AddHandler("sql", UpdateTeam)
bus.AddHandler("sql", DeleteTeam)
bus.AddHandler("sql", SearchTeams)
bus.AddHandler("sql", GetTeamById)
bus.AddHandler("sql", GetTeamsByUser)
bus.AddHandler("sql", AddTeamMember)
bus.AddHandler("sql", UpdateTeamMember)
bus.AddHandler("sql", RemoveTeamMember)
bus.AddHandler("sql", GetTeamMembers)
@@ -43,7 +42,7 @@ func getFilteredUsers(signedInUser *models.SignedInUser, hiddenUsers map[string]
func getTeamMemberCount(filteredUsers []string) string {
if len(filteredUsers) > 0 {
return `(SELECT COUNT(*) FROM team_member
return `(SELECT COUNT(*) FROM team_member
INNER JOIN ` + dialect.Quote("user") + ` ON team_member.user_id = ` + dialect.Quote("user") + `.id
WHERE team_member.team_id = team.id AND ` + dialect.Quote("user") + `.login NOT IN (?` +
strings.Repeat(",?", len(filteredUsers)-1) + ")" +
@@ -75,28 +74,25 @@ func getTeamSelectSQLBase(filteredUsers []string) string {
` FROM team as team `
}
func CreateTeam(cmd *models.CreateTeamCommand) error {
return inTransaction(func(sess *DBSession) error {
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, 0, sess); err != nil {
func (ss *SQLStore) CreateTeam(name, email string, orgID int64) (models.Team, error) {
team := models.Team{
Name: name,
Email: email,
OrgId: orgID,
Created: time.Now(),
Updated: time.Now(),
}
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
if isNameTaken, err := isTeamNameTaken(orgID, name, 0, sess); err != nil {
return err
} else if isNameTaken {
return models.ErrTeamNameTaken
}
team := models.Team{
Name: cmd.Name,
Email: cmd.Email,
OrgId: cmd.OrgId,
Created: time.Now(),
Updated: time.Now(),
}
_, err := sess.Insert(&team)
cmd.Result = team
return err
})
return team, err
}
func UpdateTeam(cmd *models.UpdateTeamCommand) error {
@@ -152,8 +148,8 @@ func DeleteTeam(cmd *models.DeleteTeamCommand) error {
})
}
func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) {
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgId, teamId); err != nil {
func teamExists(orgID int64, teamID int64, sess *DBSession) (bool, error) {
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgID, teamID); err != nil {
return false, err
} else if len(res) != 1 {
return false, models.ErrTeamNotFound
@@ -165,7 +161,6 @@ func teamExists(orgId int64, teamId int64, sess *DBSession) (bool, error) {
func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) {
var team models.Team
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
if err != nil {
return false, nil
}
@@ -283,26 +278,27 @@ func GetTeamsByUser(query *models.GetTeamsByUserQuery) error {
}
// AddTeamMember adds a user to a team
func AddTeamMember(cmd *models.AddTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) error {
if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?", cmd.OrgId, cmd.TeamId, cmd.UserId); err != nil {
func (ss *SQLStore) AddTeamMember(userID, orgID, teamID int64, isExternal bool, permission models.PermissionType) error {
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
if res, err := sess.Query("SELECT 1 from team_member WHERE org_id=? and team_id=? and user_id=?",
orgID, teamID, userID); err != nil {
return err
} else if len(res) == 1 {
return models.ErrTeamMemberAlreadyAdded
}
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
if _, err := teamExists(orgID, teamID, sess); err != nil {
return err
}
entity := models.TeamMember{
OrgId: cmd.OrgId,
TeamId: cmd.TeamId,
UserId: cmd.UserId,
External: cmd.External,
OrgId: orgID,
TeamId: teamID,
UserId: userID,
External: isExternal,
Created: time.Now(),
Updated: time.Now(),
Permission: cmd.Permission,
Permission: permission,
}
_, err := sess.Insert(&entity)
+70 -73
View File
@@ -14,32 +14,29 @@ import (
func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Testing Team commands & queries", t, func() {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given saved users and two teams", func() {
var userIds []int64
for i := 0; i < 5; i++ {
userCmd := &models.CreateUserCommand{
userCmd := models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i),
}
err := CreateUser(context.Background(), userCmd)
user, err := sqlStore.CreateUser(context.Background(), userCmd)
So(err, ShouldBeNil)
userIds = append(userIds, userCmd.Result.Id)
userIds = append(userIds, user.Id)
}
var testOrgId int64 = 1
group1 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group1 name", Email: "test1@test.com"}
group2 := models.CreateTeamCommand{OrgId: testOrgId, Name: "group2 name", Email: "test2@test.com"}
err := CreateTeam(&group1)
const testOrgID int64 = 1
team1, err := sqlStore.CreateTeam("group1 name", "test1@test.com", testOrgID)
So(err, ShouldBeNil)
err = CreateTeam(&group2)
team2, err := sqlStore.CreateTeam("group2 name", "test2@test.com", testOrgID)
So(err, ShouldBeNil)
Convey("Should be able to create teams and add users", func() {
query := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
query := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10}
err = SearchTeams(query)
So(err, ShouldBeNil)
So(query.Page, ShouldEqual, 1)
@@ -47,33 +44,33 @@ func TestTeamCommandsAndQueries(t *testing.T) {
team1 := query.Result.Teams[0]
So(team1.Name, ShouldEqual, "group1 name")
So(team1.Email, ShouldEqual, "test1@test.com")
So(team1.OrgId, ShouldEqual, testOrgId)
So(team1.OrgId, ShouldEqual, testOrgID)
So(team1.MemberCount, ShouldEqual, 0)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[0]})
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[1], External: true})
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, true, 0)
So(err, ShouldBeNil)
q1 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id}
q1 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
err = GetTeamMembers(q1)
So(err, ShouldBeNil)
So(q1.Result, ShouldHaveLength, 2)
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
So(q1.Result[0].Login, ShouldEqual, "loginuser0")
So(q1.Result[0].OrgId, ShouldEqual, testOrgId)
So(q1.Result[0].OrgId, ShouldEqual, testOrgID)
So(q1.Result[1].TeamId, ShouldEqual, team1.Id)
So(q1.Result[1].Login, ShouldEqual, "loginuser1")
So(q1.Result[1].OrgId, ShouldEqual, testOrgId)
So(q1.Result[1].OrgId, ShouldEqual, testOrgID)
So(q1.Result[1].External, ShouldEqual, true)
q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true}
err = GetTeamMembers(q2)
So(err, ShouldBeNil)
So(q2.Result, ShouldHaveLength, 1)
So(q2.Result[0].TeamId, ShouldEqual, team1.Id)
So(q2.Result[0].Login, ShouldEqual, "loginuser1")
So(q2.Result[0].OrgId, ShouldEqual, testOrgId)
So(q2.Result[0].OrgId, ShouldEqual, testOrgID)
So(q2.Result[0].External, ShouldEqual, true)
err = SearchTeams(query)
@@ -81,13 +78,13 @@ func TestTeamCommandsAndQueries(t *testing.T) {
team1 = query.Result.Teams[0]
So(team1.MemberCount, ShouldEqual, 2)
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: team1.Id}
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: team1.Id}
err = GetTeamById(getTeamQuery)
So(err, ShouldBeNil)
team1 = getTeamQuery.Result
So(team1.Name, ShouldEqual, "group1 name")
So(team1.Email, ShouldEqual, "test1@test.com")
So(team1.OrgId, ShouldEqual, testOrgId)
So(team1.OrgId, ShouldEqual, testOrgID)
So(team1.MemberCount, ShouldEqual, 2)
})
@@ -96,49 +93,48 @@ func TestTeamCommandsAndQueries(t *testing.T) {
err := SetAuthInfo(&models.SetAuthInfoCommand{UserId: userId, AuthModule: "oauth_github", AuthId: "1234567"})
So(err, ShouldBeNil)
teamQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Name: "group1 name", Page: 1, Limit: 10}
teamQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10}
err = SearchTeams(teamQuery)
So(err, ShouldBeNil)
So(teamQuery.Page, ShouldEqual, 1)
team1 := teamQuery.Result.Teams[0]
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userId, External: true})
err = sqlStore.AddTeamMember(userId, testOrgID, team1.Id, true, 0)
So(err, ShouldBeNil)
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team1.Id, External: true}
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true}
err = GetTeamMembers(memberQuery)
So(err, ShouldBeNil)
So(memberQuery.Result, ShouldHaveLength, 1)
So(memberQuery.Result[0].TeamId, ShouldEqual, team1.Id)
So(memberQuery.Result[0].Login, ShouldEqual, "loginuser1")
So(memberQuery.Result[0].OrgId, ShouldEqual, testOrgId)
So(memberQuery.Result[0].OrgId, ShouldEqual, testOrgID)
So(memberQuery.Result[0].External, ShouldEqual, true)
So(memberQuery.Result[0].AuthModule, ShouldEqual, "oauth_github")
})
Convey("Should be able to update users in a team", func() {
userId := userIds[0]
team := group1.Result
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userId}
err = AddTeamMember(&addMemberCmd)
team := team1
err = sqlStore.AddTeamMember(userId, testOrgID, team.Id, false, 0)
So(err, ShouldBeNil)
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
err = GetTeamMembers(qBeforeUpdate)
So(err, ShouldBeNil)
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: userId,
OrgId: testOrgId,
OrgId: testOrgID,
TeamId: team.Id,
Permission: models.PERMISSION_ADMIN,
})
So(err, ShouldBeNil)
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
err = GetTeamMembers(qAfterUpdate)
So(err, ShouldBeNil)
So(qAfterUpdate.Result[0].Permission, ShouldEqual, models.PERMISSION_ADMIN)
@@ -146,12 +142,11 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Should default to member permission level when updating a user with invalid permission level", func() {
userID := userIds[0]
team := group1.Result
addMemberCmd := models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team.Id, UserId: userID}
err = AddTeamMember(&addMemberCmd)
team := team1
err = sqlStore.AddTeamMember(userID, testOrgID, team.Id, false, 0)
So(err, ShouldBeNil)
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
err = GetTeamMembers(qBeforeUpdate)
So(err, ShouldBeNil)
So(qBeforeUpdate.Result[0].Permission, ShouldEqual, 0)
@@ -159,14 +154,14 @@ func TestTeamCommandsAndQueries(t *testing.T) {
invalidPermissionLevel := models.PERMISSION_EDIT
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: userID,
OrgId: testOrgId,
OrgId: testOrgID,
TeamId: team.Id,
Permission: invalidPermissionLevel,
})
So(err, ShouldBeNil)
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: team.Id}
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id}
err = GetTeamMembers(qAfterUpdate)
So(err, ShouldBeNil)
So(qAfterUpdate.Result[0].Permission, ShouldEqual, 0)
@@ -175,8 +170,8 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Shouldn't be able to update a user not in the team.", func() {
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{
UserId: 1,
OrgId: testOrgId,
TeamId: group1.Result.Id,
OrgId: testOrgID,
TeamId: team1.Id,
Permission: models.PERMISSION_ADMIN,
})
@@ -184,24 +179,24 @@ func TestTeamCommandsAndQueries(t *testing.T) {
})
Convey("Should be able to search for teams", func() {
query := &models.SearchTeamsQuery{OrgId: testOrgId, Query: "group", Page: 1}
query := &models.SearchTeamsQuery{OrgId: testOrgID, Query: "group", Page: 1}
err = SearchTeams(query)
So(err, ShouldBeNil)
So(len(query.Result.Teams), ShouldEqual, 2)
So(query.Result.TotalCount, ShouldEqual, 2)
query2 := &models.SearchTeamsQuery{OrgId: testOrgId, Query: ""}
query2 := &models.SearchTeamsQuery{OrgId: testOrgID, Query: ""}
err = SearchTeams(query2)
So(err, ShouldBeNil)
So(len(query2.Result.Teams), ShouldEqual, 2)
})
Convey("Should be able to return all teams a user is member of", func() {
groupId := group2.Result.Id
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
groupId := team2.Id
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
So(err, ShouldBeNil)
query := &models.GetTeamsByUserQuery{OrgId: testOrgId, UserId: userIds[0]}
query := &models.GetTeamsByUserQuery{OrgId: testOrgID, UserId: userIds[0]}
err = GetTeamsByUser(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
@@ -210,63 +205,65 @@ func TestTeamCommandsAndQueries(t *testing.T) {
})
Convey("Should be able to remove users from a group", func() {
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
So(err, ShouldBeNil)
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0]})
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
So(err, ShouldBeNil)
q2 := &models.GetTeamMembersQuery{OrgId: testOrgId, TeamId: group1.Result.Id}
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id}
err = GetTeamMembers(q2)
So(err, ShouldBeNil)
So(len(q2.Result), ShouldEqual, 0)
})
Convey("When ProtectLastAdmin is set to true", func() {
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: models.PERMISSION_ADMIN})
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
So(err, ShouldBeNil)
Convey("A user should not be able to remove the last admin", func() {
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], ProtectLastAdmin: true})
So(err, ShouldEqual, models.ErrLastTeamAdmin)
})
Convey("A user should be able to remove an admin if there are other admins", func() {
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
So(err, ShouldBeNil)
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], ProtectLastAdmin: true})
err = RemoveTeamMember(&models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], ProtectLastAdmin: true})
So(err, ShouldBeNil)
})
Convey("A user should not be able to remove the admin permission for the last admin", func() {
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
So(err, ShouldBeError, models.ErrLastTeamAdmin)
})
Convey("A user should be able to remove the admin permission if there are other admins", func() {
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
So(err, ShouldBeNil)
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgId, TeamId: group1.Result.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
err = UpdateTeamMember(&models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0, ProtectLastAdmin: true})
So(err, ShouldBeNil)
})
})
Convey("Should be able to remove a group with users and permissions", func() {
groupId := group2.Result.Id
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1]})
groupId := team2.Id
err := sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, 0)
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[2]})
err = sqlStore.AddTeamMember(userIds[2], testOrgID, groupId, false, 0)
So(err, ShouldBeNil)
err = testHelperUpdateDashboardAcl(1, models.DashboardAcl{DashboardID: 1, OrgID: testOrgId, Permission: models.PERMISSION_EDIT, TeamID: groupId})
err = testHelperUpdateDashboardAcl(t, sqlStore, 1, models.DashboardAcl{
DashboardID: 1, OrgID: testOrgID, Permission: models.PERMISSION_EDIT, TeamID: groupId,
})
So(err, ShouldBeNil)
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgId, Id: groupId})
err = DeleteTeam(&models.DeleteTeamCommand{OrgId: testOrgID, Id: groupId})
So(err, ShouldBeNil)
query := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: groupId}
query := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: groupId}
err = GetTeamById(query)
So(err, ShouldEqual, models.ErrTeamNotFound)
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgId}
permQuery := &models.GetDashboardAclInfoListQuery{DashboardID: 1, OrgID: testOrgID}
err = GetDashboardAclInfoList(permQuery)
So(err, ShouldBeNil)
@@ -274,18 +271,18 @@ func TestTeamCommandsAndQueries(t *testing.T) {
})
Convey("Should be able to return if user is admin of teams or not", func() {
groupId := group2.Result.Id
err := AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[0]})
groupId := team2.Id
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: groupId, UserId: userIds[1], Permission: models.PERMISSION_ADMIN})
err = sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, models.PERMISSION_ADMIN)
So(err, ShouldBeNil)
query := &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[0]}}
query := &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgID, UserId: userIds[0]}}
err = IsAdminOfTeams(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeFalse)
query = &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgId, UserId: userIds[1]}}
query = &models.IsAdminOfTeamsQuery{SignedInUser: &models.SignedInUser{OrgId: testOrgID, UserId: userIds[1]}}
err = IsAdminOfTeams(query)
So(err, ShouldBeNil)
So(query.Result, ShouldBeTrue)
@@ -295,29 +292,29 @@ func TestTeamCommandsAndQueries(t *testing.T) {
signedInUser := &models.SignedInUser{Login: "loginuser0"}
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
teamId := group1.Result.Id
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[0]})
teamId := team1.Id
err = sqlStore.AddTeamMember(userIds[0], testOrgID, teamId, false, 0)
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[1]})
err = sqlStore.AddTeamMember(userIds[1], testOrgID, teamId, false, 0)
So(err, ShouldBeNil)
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[2]})
err = sqlStore.AddTeamMember(userIds[2], testOrgID, teamId, false, 0)
So(err, ShouldBeNil)
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
err = SearchTeams(searchQuery)
So(err, ShouldBeNil)
So(searchQuery.Result.Teams, ShouldHaveLength, 2)
team1 := searchQuery.Result.Teams[0]
So(team1.MemberCount, ShouldEqual, 2)
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
err = SearchTeams(searchQueryFilteredByUser)
So(err, ShouldBeNil)
So(searchQueryFilteredByUser.Result.Teams, ShouldHaveLength, 1)
team1 = searchQuery.Result.Teams[0]
So(team1.MemberCount, ShouldEqual, 2)
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
err = GetTeamById(getTeamQuery)
So(err, ShouldBeNil)
So(getTeamQuery.Result.MemberCount, ShouldEqual, 2)
+17 -15
View File
@@ -31,12 +31,10 @@ func (ss *SQLStore) addUserQueryAndCommandHandlers() {
bus.AddHandler("sql", DisableUser)
bus.AddHandler("sql", BatchDisableUsers)
bus.AddHandler("sql", DeleteUser)
bus.AddHandler("sql", UpdateUserPermissions)
bus.AddHandler("sql", SetUserHelpFlag)
bus.AddHandlerCtx("sql", CreateUser)
}
func getOrgIdForNewUser(sess *DBSession, cmd *models.CreateUserCommand) (int64, error) {
func getOrgIdForNewUser(sess *DBSession, cmd models.CreateUserCommand) (int64, error) {
if cmd.SkipOrgSetup {
return -1, nil
}
@@ -191,8 +189,9 @@ func (ss *SQLStore) createUser(ctx context.Context, args userCreationArgs, skipO
return user, nil
}
func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
return inTransactionCtx(ctx, func(sess *DBSession) error {
func (ss *SQLStore) CreateUser(ctx context.Context, cmd models.CreateUserCommand) (*models.User, error) {
var user *models.User
err := ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
orgId, err := getOrgIdForNewUser(sess, cmd)
if err != nil {
return err
@@ -202,13 +201,16 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
cmd.Email = cmd.Login
}
exists, _ := sess.Where("email=? OR login=?", cmd.Email, cmd.Login).Get(&models.User{})
exists, err := sess.Where("email=? OR login=?", cmd.Email, cmd.Login).Get(&models.User{})
if err != nil {
return err
}
if exists {
return models.ErrUserAlreadyExists
}
// create user
user := models.User{
user = &models.User{
Email: cmd.Email,
Name: cmd.Name,
Login: cmd.Login,
@@ -243,7 +245,7 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
sess.UseBool("is_admin")
if _, err := sess.Insert(&user); err != nil {
if _, err := sess.Insert(user); err != nil {
return err
}
@@ -255,8 +257,6 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
Email: user.Email,
})
cmd.Result = user
// create org user link
if !cmd.SkipOrgSetup {
orgUser := models.OrgUser{
@@ -282,6 +282,8 @@ func CreateUser(ctx context.Context, cmd *models.CreateUserCommand) error {
return nil
})
return user, err
}
func GetUserById(query *models.GetUserByIdQuery) error {
@@ -729,14 +731,14 @@ func deleteUserInTransaction(sess *DBSession, cmd *models.DeleteUserCommand) err
return nil
}
func UpdateUserPermissions(cmd *models.UpdateUserPermissionsCommand) error {
return inTransaction(func(sess *DBSession) error {
user := models.User{}
if _, err := sess.ID(cmd.UserId).Get(&user); err != nil {
func (ss *SQLStore) UpdateUserPermissions(userID int64, isAdmin bool) error {
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
var user models.User
if _, err := sess.ID(userID).Get(&user); err != nil {
return err
}
user.IsAdmin = cmd.IsGrafanaAdmin
user.IsAdmin = isAdmin
sess.UseBool("is_admin")
_, err := sess.ID(user.Id).Update(&user)
+7 -9
View File
@@ -15,18 +15,16 @@ import (
//nolint:goconst
func TestUserAuth(t *testing.T) {
InitTestDB(t)
sqlStore := InitTestDB(t)
Convey("Given 5 users", t, func() {
var err error
var cmd *models.CreateUserCommand
for i := 0; i < 5; i++ {
cmd = &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i),
}
err = CreateUser(context.Background(), cmd)
_, err := sqlStore.CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
}
@@ -46,7 +44,7 @@ func TestUserAuth(t *testing.T) {
login := "loginuser0"
query := &models.GetUserByAuthInfoQuery{Login: login}
err = GetUserByAuthInfo(query)
err := GetUserByAuthInfo(query)
So(err, ShouldBeNil)
So(query.Result.Login, ShouldEqual, login)
@@ -82,7 +80,7 @@ func TestUserAuth(t *testing.T) {
Convey("Can set & locate by AuthModule and AuthId", func() {
// get nonexistent user_auth entry
query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test"}
err = GetUserByAuthInfo(query)
err := GetUserByAuthInfo(query)
So(err, ShouldEqual, models.ErrUserNotFound)
So(query.Result, ShouldBeNil)
@@ -144,7 +142,7 @@ func TestUserAuth(t *testing.T) {
// Calling GetUserByAuthInfoQuery on an existing user will populate an entry in the user_auth table
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test", AuthId: "test"}
err = GetUserByAuthInfo(query)
err := GetUserByAuthInfo(query)
So(err, ShouldBeNil)
So(query.Result.Login, ShouldEqual, login)
@@ -179,7 +177,7 @@ func TestUserAuth(t *testing.T) {
// Make the first log-in during the past
getTime = func() time.Time { return time.Now().AddDate(0, 0, -2) }
query := &models.GetUserByAuthInfoQuery{Login: login, AuthModule: "test1", AuthId: "test1"}
err = GetUserByAuthInfo(query)
err := GetUserByAuthInfo(query)
getTime = time.Now
So(err, ShouldBeNil)
+34 -37
View File
@@ -20,17 +20,17 @@ func TestUserDataAccess(t *testing.T) {
ss := InitTestDB(t)
Convey("Creates a user", func() {
cmd := &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: "usertest@test.com",
Name: "user name",
Login: "user_test_login",
}
err := CreateUser(context.Background(), cmd)
user, err := ss.CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
Convey("Loading a user", func() {
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
query := models.GetUserByIdQuery{Id: user.Id}
err := GetUserById(&query)
So(err, ShouldBeNil)
@@ -43,18 +43,18 @@ func TestUserDataAccess(t *testing.T) {
})
Convey("Creates disabled user", func() {
cmd := &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: "usertest@test.com",
Name: "user name",
Login: "user_test_login",
IsDisabled: true,
}
err := CreateUser(context.Background(), cmd)
user, err := ss.CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
Convey("Loading a user", func() {
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
query := models.GetUserByIdQuery{Id: user.Id}
err := GetUserById(&query)
So(err, ShouldBeNil)
@@ -78,18 +78,18 @@ func TestUserDataAccess(t *testing.T) {
So(err, ShouldBeNil)
Convey("Creates user assigned to other organization", func() {
cmd := &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: "usertest@test.com",
Name: "user name",
Login: "user_test_login",
OrgId: orgCmd.Result.Id,
}
err := CreateUser(context.Background(), cmd)
user, err := ss.CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
Convey("Loading a user", func() {
query := models.GetUserByIdQuery{Id: cmd.Result.Id}
query := models.GetUserByIdQuery{Id: user.Id}
err := GetUserById(&query)
So(err, ShouldBeNil)
@@ -104,20 +104,20 @@ func TestUserDataAccess(t *testing.T) {
Convey("Don't create user assigned to unknown organization", func() {
const nonExistingOrgID = 10000
cmd := &models.CreateUserCommand{
cmd := models.CreateUserCommand{
Email: "usertest@test.com",
Name: "user name",
Login: "user_test_login",
OrgId: nonExistingOrgID,
}
err := CreateUser(context.Background(), cmd)
_, err := ss.CreateUser(context.Background(), cmd)
So(err, ShouldEqual, models.ErrOrgNotFound)
})
})
Convey("Given 5 users", func() {
users := createFiveTestUsers(func(i int) *models.CreateUserCommand {
users := createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -238,7 +238,7 @@ func TestUserDataAccess(t *testing.T) {
Convey("Can return list users based on their is_disabled flag", func() {
ss = InitTestDB(t)
createFiveTestUsers(func(i int) *models.CreateUserCommand {
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -269,7 +269,7 @@ func TestUserDataAccess(t *testing.T) {
So(third, ShouldBeTrue)
ss = InitTestDB(t)
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -286,7 +286,7 @@ func TestUserDataAccess(t *testing.T) {
})
So(err, ShouldBeNil)
err = testHelperUpdateDashboardAcl(1, models.DashboardAcl{
err = testHelperUpdateDashboardAcl(t, ss, 1, models.DashboardAcl{
DashboardID: 1, OrgID: users[0].OrgId, UserID: users[1].Id,
Permission: models.PERMISSION_EDIT,
})
@@ -365,7 +365,7 @@ func TestUserDataAccess(t *testing.T) {
Convey("Should enable all users", func() {
ss = InitTestDB(t)
createFiveTestUsers(func(i int) *models.CreateUserCommand {
createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -392,7 +392,7 @@ func TestUserDataAccess(t *testing.T) {
Convey("Should disable only specific users", func() {
ss = InitTestDB(t)
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -438,7 +438,7 @@ func TestUserDataAccess(t *testing.T) {
// Since previous tests were destructive
ss = InitTestDB(t)
users = createFiveTestUsers(func(i int) *models.CreateUserCommand {
users = createFiveTestUsers(t, ss, func(i int) *models.CreateUserCommand {
return &models.CreateUserCommand{
Email: fmt.Sprint("user", i, "@test.com"),
Name: fmt.Sprint("user", i),
@@ -546,25 +546,22 @@ func TestUserDataAccess(t *testing.T) {
})
Convey("Given one grafana admin user", func() {
var err error
createUserCmd := &models.CreateUserCommand{
createUserCmd := models.CreateUserCommand{
Email: fmt.Sprint("admin", "@test.com"),
Name: "admin",
Login: "admin",
IsAdmin: true,
}
err = CreateUser(context.Background(), createUserCmd)
user, err := ss.CreateUser(context.Background(), createUserCmd)
So(err, ShouldBeNil)
Convey("Cannot make themselves a non-admin", func() {
updateUserPermsCmd := models.UpdateUserPermissionsCommand{IsGrafanaAdmin: false, UserId: 1}
updatePermsError := UpdateUserPermissions(&updateUserPermsCmd)
updatePermsError := ss.UpdateUserPermissions(1, false)
So(updatePermsError, ShouldEqual, models.ErrLastGrafanaAdmin)
query := models.GetUserByIdQuery{Id: createUserCmd.Result.Id}
query := models.GetUserByIdQuery{Id: user.Id}
getUserError := GetUserById(&query)
So(getUserError, ShouldBeNil)
So(query.Result.IsAdmin, ShouldEqual, true)
@@ -574,33 +571,33 @@ func TestUserDataAccess(t *testing.T) {
Convey("Given one user", func() {
const email = "user@test.com"
const username = "user"
createUserCmd := &models.CreateUserCommand{
createUserCmd := models.CreateUserCommand{
Email: email,
Name: "user",
Login: username,
}
err := CreateUser(context.Background(), createUserCmd)
_, err := ss.CreateUser(context.Background(), createUserCmd)
So(err, ShouldBeNil)
Convey("When trying to create a new user with the same email, an error is returned", func() {
createUserCmd := &models.CreateUserCommand{
createUserCmd := models.CreateUserCommand{
Email: email,
Name: "user2",
Login: "user2",
SkipOrgSetup: true,
}
err := CreateUser(context.Background(), createUserCmd)
_, err := ss.CreateUser(context.Background(), createUserCmd)
So(err, ShouldEqual, models.ErrUserAlreadyExists)
})
Convey("When trying to create a new user with the same login, an error is returned", func() {
createUserCmd := &models.CreateUserCommand{
createUserCmd := models.CreateUserCommand{
Email: "user2@test.com",
Name: "user2",
Login: username,
SkipOrgSetup: true,
}
err := CreateUser(context.Background(), createUserCmd)
_, err := ss.CreateUser(context.Background(), createUserCmd)
So(err, ShouldEqual, models.ErrUserAlreadyExists)
})
})
@@ -618,15 +615,15 @@ func GetOrgUsersForTest(query *models.GetOrgUsersQuery) error {
return err
}
func createFiveTestUsers(fn func(i int) *models.CreateUserCommand) []models.User {
var err error
var cmd *models.CreateUserCommand
func createFiveTestUsers(t *testing.T, sqlStore *SQLStore, fn func(i int) *models.CreateUserCommand) []models.User {
t.Helper()
users := []models.User{}
for i := 0; i < 5; i++ {
cmd = fn(i)
cmd := fn(i)
err = CreateUser(context.Background(), cmd)
users = append(users, cmd.Result)
user, err := sqlStore.CreateUser(context.Background(), *cmd)
users = append(users, *user)
So(err, ShouldBeNil)
}