Chore: Copy methods from sqlstore to org store (#55615)

* Chore: Copy methods from sqlstore to org store

* Rename method, add test

* Add comments of tests
This commit is contained in:
idafurjes 2022-09-22 19:02:55 +02:00 committed by GitHub
parent e981848026
commit 591df92265
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 246 additions and 98 deletions

View File

@ -50,7 +50,7 @@ type CreateOrgCommand struct {
Name string `json:"name" binding:"Required"`
// initial admin user for account
UserID int64 `json:"-"`
UserID int64 `json:"-" xorm:"user_id"`
}
type GetOrgIDForNewUserCommand struct {
@ -62,11 +62,11 @@ type GetOrgIDForNewUserCommand struct {
}
type GetUserOrgListQuery struct {
UserID int64
UserID int64 `xorm:"user_id"`
}
type UserOrgDTO struct {
OrgID int64 `json:"orgId"`
OrgID int64 `json:"orgId" xorm:"org_id"`
Name string `json:"name"`
Role RoleType `json:"role"`
}
@ -81,11 +81,11 @@ type SearchOrgsQuery struct {
Name string
Limit int
Page int
IDs []int64
IDs []int64 `xorm:"ids"`
}
type OrgDTO struct {
ID int64 `json:"id"`
ID int64 `json:"id" xorm:"id"`
Name string `json:"name"`
}
@ -168,3 +168,24 @@ func (r *RoleType) UnmarshalText(data []byte) error {
return nil
}
type ByOrgName []*UserOrgDTO
// Len returns the length of an array of organisations.
func (o ByOrgName) Len() int {
return len(o)
}
// Swap swaps two indices of an array of organizations.
func (o ByOrgName) Swap(i, j int) {
o[i], o[j] = o[j], o[i]
}
// Less returns whether element i of an array of organizations is less than element j.
func (o ByOrgName) Less(i, j int) bool {
if strings.ToLower(o[i].Name) < strings.ToLower(o[j].Name) {
return true
}
return o[i].Name < o[j].Name
}

View File

@ -14,8 +14,7 @@ type Service interface {
GetByID(context.Context, *GetOrgByIdQuery) (*Org, error)
GetByNameHandler(context.Context, *GetOrgByNameQuery) (*Org, error)
GetByName(string) (*Org, error)
CreateWithMember(string, int64) (*Org, error)
Create(context.Context, *CreateOrgCommand) (*Org, error)
CreateWithMember(context.Context, *CreateOrgCommand) (*Org, error)
UpdateAddress(context.Context, *UpdateOrgAddressCommand) error
Delete(context.Context, *DeleteOrgCommand) error
GetOrCreate(context.Context, string) (int64, error)

View File

@ -85,52 +85,19 @@ func (s *Service) DeleteUserFromAll(ctx context.Context, userID int64) error {
return s.store.DeleteUserFromAll(ctx, userID)
}
// TODO: remove wrapper around sqlstore
// TODO: refactor service to call store CRUD method
func (s *Service) GetUserOrgList(ctx context.Context, query *org.GetUserOrgListQuery) ([]*org.UserOrgDTO, error) {
q := &models.GetUserOrgListQuery{
UserId: query.UserID,
}
err := s.sqlStore.GetUserOrgList(ctx, q)
if err != nil {
return nil, err
}
var result []*org.UserOrgDTO
for _, orga := range q.Result {
result = append(result, &org.UserOrgDTO{
OrgID: orga.OrgId,
Name: orga.Name,
Role: orga.Role,
})
}
return result, nil
return s.store.GetUserOrgList(ctx, query)
}
// TODO: refactor service to call store CRUD method
func (s *Service) UpdateOrg(ctx context.Context, cmd *org.UpdateOrgCommand) error {
return s.store.Update(ctx, cmd)
}
// TODO: remove wrapper around sqlstore
// TODO: refactor service to call store CRUD method
func (s *Service) Search(ctx context.Context, query *org.SearchOrgsQuery) ([]*org.OrgDTO, error) {
var res []*org.OrgDTO
q := &models.SearchOrgsQuery{
Query: query.Query,
Name: query.Name,
Limit: query.Limit,
Page: query.Page,
Ids: query.IDs,
}
err := s.sqlStore.SearchOrgs(ctx, q)
if err != nil {
return nil, err
}
for _, r := range q.Result {
res = append(res, &org.OrgDTO{
ID: r.Id,
Name: r.Name,
})
}
return res, nil
return s.store.Search(ctx, query)
}
// TODO: remove wrapper around sqlstore
@ -198,50 +165,9 @@ func (s *Service) GetByName(name string) (*org.Org, error) {
}, nil
}
// TODO: remove wrapper around sqlstore
func (s *Service) CreateWithMember(name string, userID int64) (*org.Org, error) {
orga, err := s.sqlStore.CreateOrgWithMember(name, userID)
if err != nil {
return nil, err
}
return &org.Org{
ID: orga.Id,
Version: orga.Version,
Name: orga.Name,
Address1: orga.Address1,
Address2: orga.Address2,
City: orga.City,
ZipCode: orga.ZipCode,
State: orga.State,
Country: orga.Country,
Created: orga.Created,
Updated: orga.Updated,
}, nil
}
// TODO: remove wrapper around sqlstore
func (s *Service) Create(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
q := &models.CreateOrgCommand{
Name: cmd.Name,
UserId: cmd.UserID,
}
err := s.sqlStore.CreateOrg(ctx, q)
if err != nil {
return nil, err
}
return &org.Org{
ID: q.Result.Id,
Version: q.Result.Version,
Name: q.Result.Name,
Address1: q.Result.Address1,
Address2: q.Result.Address2,
City: q.Result.City,
ZipCode: q.Result.ZipCode,
State: q.Result.State,
Country: q.Result.Country,
Created: q.Result.Created,
Updated: q.Result.Updated,
}, nil
// TODO: refactor service to call store CRUD method
func (s *Service) CreateWithMember(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
return s.store.CreateWithMember(ctx, cmd)
}
// TODO: refactor service to call store CRUD method

View File

@ -54,10 +54,12 @@ func TestOrgService(t *testing.T) {
}
type FakeOrgStore struct {
ExpectedOrg *org.Org
ExpectedOrgID int64
ExpectedUserID int64
ExpectedError error
ExpectedOrg *org.Org
ExpectedOrgID int64
ExpectedUserID int64
ExpectedError error
ExpectedUserOrgs []*org.UserOrgDTO
ExpectedOrgs []*org.OrgDTO
}
func newOrgStoreFake() *FakeOrgStore {
@ -91,3 +93,15 @@ func (f *FakeOrgStore) UpdateAddress(ctx context.Context, cmd *org.UpdateOrgAddr
func (f *FakeOrgStore) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error {
return f.ExpectedError
}
func (f *FakeOrgStore) GetUserOrgList(ctx context.Context, query *org.GetUserOrgListQuery) ([]*org.UserOrgDTO, error) {
return f.ExpectedUserOrgs, f.ExpectedError
}
func (f *FakeOrgStore) Search(ctx context.Context, query *org.SearchOrgsQuery) ([]*org.OrgDTO, error) {
return f.ExpectedOrgs, f.ExpectedError
}
func (f *FakeOrgStore) CreateWithMember(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
return f.ExpectedOrg, f.ExpectedError
}

View File

@ -2,6 +2,8 @@ package orgimpl
import (
"context"
"fmt"
"sort"
"time"
"github.com/grafana/grafana/pkg/events"
@ -24,6 +26,9 @@ type store interface {
// TO BE REFACTORED - move logic to service methods and leave CRUD methods for store
UpdateAddress(context.Context, *org.UpdateOrgAddressCommand) error
Delete(context.Context, *org.DeleteOrgCommand) error
GetUserOrgList(context.Context, *org.GetUserOrgListQuery) ([]*org.UserOrgDTO, error)
Search(context.Context, *org.SearchOrgsQuery) ([]*org.OrgDTO, error)
CreateWithMember(context.Context, *org.CreateOrgCommand) (*org.Org, error)
}
type sqlStore struct {
@ -217,3 +222,100 @@ func (ss *sqlStore) Delete(ctx context.Context, cmd *org.DeleteOrgCommand) error
return nil
})
}
// TODO: refactor move logic to service method
func (ss *sqlStore) GetUserOrgList(ctx context.Context, query *org.GetUserOrgListQuery) ([]*org.UserOrgDTO, error) {
result := make([]*org.UserOrgDTO, 0)
err := ss.db.WithDbSession(ctx, func(dbSess *sqlstore.DBSession) error {
sess := dbSess.Table("org_user")
sess.Join("INNER", "org", "org_user.org_id=org.id")
sess.Join("INNER", ss.dialect.Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", ss.dialect.Quote("user")))
sess.Where("org_user.user_id=?", query.UserID)
sess.Where(ss.notServiceAccountFilter())
sess.Cols("org.name", "org_user.role", "org_user.org_id")
sess.OrderBy("org.name")
err := sess.Find(&result)
sort.Sort(org.ByOrgName(result))
return err
})
if err != nil {
return nil, err
}
return result, nil
}
func (ss *sqlStore) notServiceAccountFilter() string {
return fmt.Sprintf("%s.is_service_account = %s",
ss.dialect.Quote("user"),
ss.dialect.BooleanStr(false))
}
func (ss *sqlStore) Search(ctx context.Context, query *org.SearchOrgsQuery) ([]*org.OrgDTO, error) {
result := make([]*org.OrgDTO, 0)
err := ss.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
sess := dbSession.Table("org")
if query.Query != "" {
sess.Where("name LIKE ?", query.Query+"%")
}
if query.Name != "" {
sess.Where("name=?", query.Name)
}
if len(query.IDs) > 0 {
sess.In("id", query.IDs)
}
if query.Limit > 0 {
sess.Limit(query.Limit, query.Limit*query.Page)
}
sess.Cols("id", "name")
err := sess.Find(&result)
return err
})
if err != nil {
return nil, err
}
return result, nil
}
// CreateWithMember creates an organization with a certain name and a certain user as member.
func (ss *sqlStore) CreateWithMember(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
orga := org.Org{
Name: cmd.Name,
Created: time.Now(),
Updated: time.Now(),
}
if err := ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if isNameTaken, err := isOrgNameTaken(cmd.Name, 0, sess); err != nil {
return err
} else if isNameTaken {
return models.ErrOrgNameTaken
}
if _, err := sess.Insert(&orga); err != nil {
return err
}
user := org.OrgUser{
OrgID: orga.ID,
UserID: cmd.UserID,
Role: org.RoleAdmin,
Created: time.Now(),
Updated: time.Now(),
}
_, err := sess.Insert(&user)
sess.PublishAfterCommit(&events.OrgCreated{
Timestamp: orga.Created,
Id: orga.ID,
Name: orga.Name,
})
return err
}); err != nil {
return &orga, err
}
return &orga, nil
}

View File

@ -2,6 +2,7 @@ package orgimpl
import (
"context"
"fmt"
"testing"
"time"
@ -94,6 +95,58 @@ func TestIntegrationOrgDataAccess(t *testing.T) {
// err = orgStore.GetSignedInUser(context.Background(), &models.GetSignedInUserQuery{UserId: ac2.ID})
// require.Equal(t, err, user.ErrUserNotFound)
})
t.Run("Given we have organizations, we can query them by IDs", func(t *testing.T) {
var err error
var cmd *org.CreateOrgCommand
ids := []int64{}
for i := 1; i < 4; i++ {
cmd = &org.CreateOrgCommand{Name: fmt.Sprint("Org #", i)}
result, err := orgStore.CreateWithMember(context.Background(), cmd)
require.NoError(t, err)
ids = append(ids, result.ID)
}
query := &org.SearchOrgsQuery{IDs: ids}
result, err := orgStore.Search(context.Background(), query)
require.NoError(t, err)
require.Equal(t, len(result), 3)
})
t.Run("Given we have organizations, we can limit and paginate search", func(t *testing.T) {
ss = sqlstore.InitTestDB(t)
for i := 1; i < 4; i++ {
cmd := &org.CreateOrgCommand{Name: fmt.Sprint("Orga #", i)}
_, err := orgStore.CreateWithMember(context.Background(), cmd)
require.NoError(t, err)
}
t.Run("Should be able to search with defaults", func(t *testing.T) {
query := &org.SearchOrgsQuery{}
result, err := orgStore.Search(context.Background(), query)
require.NoError(t, err)
require.Equal(t, len(result), 3)
})
t.Run("Should be able to limit search", func(t *testing.T) {
query := &org.SearchOrgsQuery{Limit: 1}
result, err := orgStore.Search(context.Background(), query)
require.NoError(t, err)
require.Equal(t, len(result), 1)
})
t.Run("Should be able to limit and paginate search", func(t *testing.T) {
query := &org.SearchOrgsQuery{Limit: 2, Page: 1}
result, err := orgStore.Search(context.Background(), query)
require.NoError(t, err)
require.Equal(t, len(result), 1)
})
})
}
func TestIntegrationOrgUserDataAccess(t *testing.T) {
@ -103,7 +156,8 @@ func TestIntegrationOrgUserDataAccess(t *testing.T) {
ss := sqlstore.InitTestDB(t)
orgUserStore := sqlStore{
db: ss,
db: ss,
dialect: ss.GetDialect(),
}
t.Run("org user inserted", func(t *testing.T) {
@ -121,4 +175,39 @@ func TestIntegrationOrgUserDataAccess(t *testing.T) {
err := orgUserStore.DeleteUserFromAll(context.Background(), 1)
require.NoError(t, err)
})
// TODO: these test will be added when store will be CRUD
// t.Run("Given single org mode", func(t *testing.T) {
// sqlStore.Cfg.AutoAssignOrg = true
// sqlStore.Cfg.AutoAssignOrgId = 1
// sqlStore.Cfg.AutoAssignOrgRole = "Viewer"
// t.Run("Users should be added to default organization", func(t *testing.T) {
// ac1cmd := user.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
// ac2cmd := user.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
// ac1, err := sqlStore.CreateUser(context.Background(), ac1cmd)
// require.NoError(t, err)
// ac2, err := sqlStore.CreateUser(context.Background(), ac2cmd)
// require.NoError(t, err)
// q1 := models.GetUserOrgListQuery{UserId: ac1.ID}
// q2 := models.GetUserOrgListQuery{UserId: ac2.ID}
// err = sqlStore.GetUserOrgList(context.Background(), &q1)
// require.NoError(t, err)
// err = sqlStore.GetUserOrgList(context.Background(), &q2)
// require.NoError(t, err)
// require.Equal(t, q1.Result[0].OrgId, q2.Result[0].OrgId)
// require.Equal(t, string(q1.Result[0].Role), "Viewer")
// })
// })
// t.Run("Can get user organizations", func(t *testing.T) {
// query := models.GetUserOrgListQuery{UserId: ac2.ID}
// err := sqlStore.GetUserOrgList(context.Background(), &query)
// require.NoError(t, err)
// require.Equal(t, len(query.Result), 2)
// })
}

View File

@ -58,10 +58,7 @@ func (f *FakeOrgService) GetByName(name string) (*org.Org, error) {
return f.ExpectedOrg, f.ExpectedError
}
func (f *FakeOrgService) CreateWithMember(name string, userID int64) (*org.Org, error) {
return f.ExpectedOrg, f.ExpectedError
}
func (f *FakeOrgService) Create(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
func (f *FakeOrgService) CreateWithMember(ctx context.Context, cmd *org.CreateOrgCommand) (*org.Org, error) {
return f.ExpectedOrg, f.ExpectedError
}