From af67aea2a9a88f6a1c94ff179b86c90949e0a1d8 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Sun, 9 Apr 2017 00:55:07 +0200 Subject: [PATCH] WIP: add usergroup commands and queries --- pkg/models/user_group.go | 39 +++++- pkg/models/user_group_member.go | 55 ++++++++ pkg/services/sqlstore/user_group.go | 164 +++++++++++++++++++++++ pkg/services/sqlstore/user_group_test.go | 90 +++++++++++++ 4 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 pkg/models/user_group_member.go create mode 100644 pkg/services/sqlstore/user_group.go create mode 100644 pkg/services/sqlstore/user_group_test.go diff --git a/pkg/models/user_group.go b/pkg/models/user_group.go index f0b03487456..abfdad64d4b 100644 --- a/pkg/models/user_group.go +++ b/pkg/models/user_group.go @@ -1,6 +1,15 @@ package models -import "time" +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrUserGroupNotFound = errors.New("User Group not found") + ErrUserGroupNameTaken = errors.New("User Group name is taken") +) // UserGroup model type UserGroup struct { @@ -11,3 +20,31 @@ type UserGroup struct { Created time.Time Updated time.Time } + +// --------------------- +// COMMANDS + +type CreateUserGroupCommand struct { + Name string `json:"name" binding:"Required"` + OrgId int64 `json:"orgId" binding:"Required"` + + Result UserGroup `json:"-"` +} + +type DeleteUserGroupCommand struct { + Id int64 +} + +type GetUserGroupByIdQuery struct { + Id int64 + Result *UserGroup +} + +type SearchUserGroupsQuery struct { + Query string + Name string + Limit int + Page int + + Result []*UserGroup +} diff --git a/pkg/models/user_group_member.go b/pkg/models/user_group_member.go new file mode 100644 index 00000000000..cd156b490cb --- /dev/null +++ b/pkg/models/user_group_member.go @@ -0,0 +1,55 @@ +package models + +import ( + "errors" + "time" +) + +// Typed errors +var ( + ErrUserGroupMemberAlreadyAdded = errors.New("User is already added to this user group") +) + +// UserGroupMember model +type UserGroupMember struct { + Id int64 + OrgId int64 + UserGroupId int64 + UserId int64 + + Created time.Time + Updated time.Time +} + +// --------------------- +// COMMANDS + +type AddUserGroupMemberCommand struct { + OrgId int64 `json:"-"` + UserGroupId int64 `json:"-"` + UserId int64 `json:"-"` +} + +type RemoveUserGroupMemberCommand struct { + UserId int64 + UserGroupId int64 +} + +// ---------------------- +// QUERIES + +type GetUserGroupMembersQuery struct { + UserGroupId int64 + Result []*UserGroupMemberDTO +} + +// ---------------------- +// Projections and DTOs + +type UserGroupMemberDTO struct { + OrgId int64 `json:"orgId"` + UserGroupId int64 `json:"orgId"` + UserId int64 `json:"userId"` + Email string `json:"email"` + Login string `json:"login"` +} diff --git a/pkg/services/sqlstore/user_group.go b/pkg/services/sqlstore/user_group.go new file mode 100644 index 00000000000..f4d1396e82b --- /dev/null +++ b/pkg/services/sqlstore/user_group.go @@ -0,0 +1,164 @@ +package sqlstore + +import ( + "fmt" + "time" + + "github.com/go-xorm/xorm" + + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", CreateUserGroup) + bus.AddHandler("sql", DeleteUserGroup) + bus.AddHandler("sql", SearchUserGroups) + bus.AddHandler("sql", GetUserGroupById) + + bus.AddHandler("sql", AddUserGroupMember) + bus.AddHandler("sql", RemoveUserGroupMember) + bus.AddHandler("sql", GetUserGroupMembers) +} + +func CreateUserGroup(cmd *m.CreateUserGroupCommand) error { + return inTransaction2(func(sess *session) error { + + if isNameTaken, err := isUserGroupNameTaken(cmd.Name, 0, sess); err != nil { + return err + } else if isNameTaken { + return m.ErrUserGroupNameTaken + } + + userGroup := m.UserGroup{ + Name: cmd.Name, + Created: time.Now(), + Updated: time.Now(), + } + + _, err := sess.Insert(&userGroup) + + cmd.Result = userGroup + + return err + }) +} + +func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error { + return inTransaction2(func(sess *session) error { + if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.Id); err != nil { + return err + } else if len(res) != 1 { + return m.ErrUserGroupNotFound + } + + deletes := []string{ + "DELETE FROM user_group_member WHERE user_group_id = ?", + "DELETE FROM user_group WHERE id = ?", + } + + for _, sql := range deletes { + _, err := sess.Exec(sql, cmd.Id) + if err != nil { + return err + } + } + return nil + }) +} + +func isUserGroupNameTaken(name string, existingId int64, sess *session) (bool, error) { + var userGroup m.UserGroup + exists, err := sess.Where("name=?", name).Get(&userGroup) + + if err != nil { + return false, nil + } + + if exists && existingId != userGroup.Id { + return true, nil + } + + return false, nil +} + +func SearchUserGroups(query *m.SearchUserGroupsQuery) error { + query.Result = make([]*m.UserGroup, 0) + sess := x.Table("user_group") + if query.Query != "" { + sess.Where("name LIKE ?", query.Query+"%") + } + if query.Name != "" { + sess.Where("name=?", query.Name) + } + sess.Limit(query.Limit, query.Limit*query.Page) + sess.Cols("id", "name") + err := sess.Find(&query.Result) + return err +} + +func GetUserGroupById(query *m.GetUserGroupByIdQuery) error { + var userGroup m.UserGroup + exists, err := x.Id(query.Id).Get(&userGroup) + if err != nil { + return err + } + + if !exists { + return m.ErrUserGroupNotFound + } + + query.Result = &userGroup + return nil +} + +func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) error { + return inTransaction(func(sess *xorm.Session) error { + if res, err := sess.Query("SELECT 1 from user_group_member WHERE user_group_id=? and user_id=?", cmd.UserGroupId, cmd.UserId); err != nil { + return err + } else if len(res) == 1 { + return m.ErrUserGroupMemberAlreadyAdded + } + + if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.UserGroupId); err != nil { + return err + } else if len(res) != 1 { + return m.ErrUserGroupNotFound + } + + entity := m.UserGroupMember{ + OrgId: cmd.OrgId, + UserGroupId: cmd.UserGroupId, + UserId: cmd.UserId, + Created: time.Now(), + Updated: time.Now(), + } + + _, err := sess.Insert(&entity) + return err + }) +} + +func RemoveUserGroupMember(cmd *m.RemoveUserGroupMemberCommand) error { + return inTransaction(func(sess *xorm.Session) error { + var rawSql = "DELETE FROM user_group_member WHERE user_group_id=? and user_id=?" + _, err := sess.Exec(rawSql, cmd.UserGroupId, cmd.UserId) + if err != nil { + return err + } + + return err + }) +} + +func GetUserGroupMembers(query *m.GetUserGroupMembersQuery) error { + query.Result = make([]*m.UserGroupMemberDTO, 0) + sess := x.Table("user_group_member") + sess.Join("INNER", "user", fmt.Sprintf("user_group_member.user_id=%s.id", x.Dialect().Quote("user"))) + sess.Where("user_group_member.user_group_id=?", query.UserGroupId) + sess.Cols("user.org_id", "user_group_member.user_group_id", "user_group_member.user_id", "user.email", "user.login") + sess.Asc("user.email", "user.login") + + err := sess.Find(&query.Result) + return err +} diff --git a/pkg/services/sqlstore/user_group_test.go b/pkg/services/sqlstore/user_group_test.go new file mode 100644 index 00000000000..acf11a74e54 --- /dev/null +++ b/pkg/services/sqlstore/user_group_test.go @@ -0,0 +1,90 @@ +package sqlstore + +import ( + "fmt" + "testing" + + . "github.com/smartystreets/goconvey/convey" + + m "github.com/grafana/grafana/pkg/models" +) + +func TestUserGroupCommandsAndQueries(t *testing.T) { + + Convey("Testing User Group commands & queries", t, func() { + InitTestDB(t) + + Convey("Given saved users and two user groups", func() { + var userIds []int64 + for i := 0; i < 5; i++ { + userCmd := &m.CreateUserCommand{ + Email: fmt.Sprint("user", i, "@test.com"), + Name: fmt.Sprint("user", i), + Login: fmt.Sprint("loginuser", i), + } + err := CreateUser(userCmd) + So(err, ShouldBeNil) + userIds = append(userIds, userCmd.Result.Id) + } + + group1 := m.CreateUserGroupCommand{Name: "group1 name"} + group2 := m.CreateUserGroupCommand{Name: "group2 name"} + + err := CreateUserGroup(&group1) + So(err, ShouldBeNil) + err = CreateUserGroup(&group2) + So(err, ShouldBeNil) + + Convey("Should be able to create user groups and add users", func() { + query := &m.SearchUserGroupsQuery{Name: "group1 name"} + err = SearchUserGroups(query) + So(err, ShouldBeNil) + So(query.Page, ShouldEqual, 0) + + userGroup1 := query.Result[0] + So(query.Result[0].Name, ShouldEqual, "group1 name") + + err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: userGroup1.Id, UserId: userIds[0]}) + So(err, ShouldBeNil) + + q1 := &m.GetUserGroupMembersQuery{UserGroupId: userGroup1.Id} + err = GetUserGroupMembers(q1) + So(err, ShouldBeNil) + So(q1.Result[0].UserGroupId, ShouldEqual, userGroup1.Id) + So(q1.Result[0].Login, ShouldEqual, "loginuser0") + }) + + Convey("Should be able to search for user groups", func() { + query := &m.SearchUserGroupsQuery{Query: "group"} + err = SearchUserGroups(query) + So(err, ShouldBeNil) + So(len(query.Result), ShouldEqual, 2) + }) + + Convey("Should be able to remove users from a group", func() { + err = RemoveUserGroupMember(&m.RemoveUserGroupMemberCommand{UserGroupId: group1.Result.Id, UserId: userIds[0]}) + So(err, ShouldBeNil) + + q1 := &m.GetUserGroupMembersQuery{UserGroupId: group1.Result.Id} + err = GetUserGroupMembers(q1) + So(err, ShouldBeNil) + So(len(q1.Result), ShouldEqual, 0) + }) + + Convey("Should be able to remove a group with users", func() { + groupId := group2.Result.Id + err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[1]}) + So(err, ShouldBeNil) + err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[2]}) + So(err, ShouldBeNil) + + err = DeleteUserGroup(&m.DeleteUserGroupCommand{Id: groupId}) + So(err, ShouldBeNil) + + query := &m.GetUserGroupByIdQuery{Id: groupId} + err = GetUserGroupById(query) + So(err, ShouldEqual, m.ErrUserGroupNotFound) + }) + }) + }) +}