Commit 05cc1f85 by Torkel Ödegaard

Merge branch 'refactor-user-group-to-team' of…

Merge branch 'refactor-user-group-to-team' of https://github.com/alexanderzobnin/grafana into user-group-to-team
parents 10fbfcb1 26281dd9
......@@ -2,7 +2,7 @@
### WIP (in develop branch currently as its unstable or unfinished)
- Dashboard folders
- User groups
- Teams
- Dashboard permissions (on folder & dashboard level), permissions can be assigned to groups or individual users
- UX changes to nav & side menu
- New dashboard grid layout system
......
......@@ -6,7 +6,7 @@ But it will give you an idea of our current vision and plan.
### Short term (1-4 months)
- Release Grafana v5
- User groups
- Teams
- Dashboard folders
- Dashboard & folder permissions (assigned to users or groups)
- New Dashboard layout engine
......
......@@ -135,16 +135,16 @@ func (hs *HttpServer) registerRoutes() {
usersRoute.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg))
}, reqGrafanaAdmin)
// user group (admin permission required)
apiRoute.Group("/user-groups", func(userGroupsRoute RouteRegister) {
userGroupsRoute.Get("/:userGroupId", wrap(GetUserGroupById))
userGroupsRoute.Get("/search", wrap(SearchUserGroups))
userGroupsRoute.Post("/", quota("user-groups"), bind(m.CreateUserGroupCommand{}), wrap(CreateUserGroup))
userGroupsRoute.Put("/:userGroupId", bind(m.UpdateUserGroupCommand{}), wrap(UpdateUserGroup))
userGroupsRoute.Delete("/:userGroupId", wrap(DeleteUserGroupById))
userGroupsRoute.Get("/:userGroupId/members", wrap(GetUserGroupMembers))
userGroupsRoute.Post("/:userGroupId/members", quota("user-groups"), bind(m.AddUserGroupMemberCommand{}), wrap(AddUserGroupMember))
userGroupsRoute.Delete("/:userGroupId/members/:userId", wrap(RemoveUserGroupMember))
// team (admin permission required)
apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
teamsRoute.Get("/:teamId", wrap(GetTeamById))
teamsRoute.Get("/search", wrap(SearchTeams))
teamsRoute.Post("/", quota("teams"), bind(m.CreateTeamCommand{}), wrap(CreateTeam))
teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
teamsRoute.Delete("/:teamId", wrap(DeleteTeamById))
teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers))
teamsRoute.Post("/:teamId/members", quota("teams"), bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember))
teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
}, reqOrgAdmin)
// org information available to all users.
......
......@@ -43,7 +43,7 @@ func UpdateDashboardAcl(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCom
OrgId: c.OrgId,
DashboardId: dashId,
UserId: item.UserId,
UserGroupId: item.UserGroupId,
TeamId: item.TeamId,
Role: item.Role,
Permission: item.Permission,
Created: time.Now(),
......
......@@ -16,8 +16,8 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
{Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: m.PERMISSION_VIEW},
{Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: m.PERMISSION_EDIT},
{Id: 3, OrgId: 1, DashboardId: 1, UserId: 4, Permission: m.PERMISSION_ADMIN},
{Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permission: m.PERMISSION_VIEW},
{Id: 5, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permission: m.PERMISSION_ADMIN},
{Id: 4, OrgId: 1, DashboardId: 1, TeamId: 1, Permission: m.PERMISSION_VIEW},
{Id: 5, OrgId: 1, DashboardId: 1, TeamId: 2, Permission: m.PERMISSION_ADMIN},
}
dtoRes := transformDashboardAclsToDTOs(mockResult)
......@@ -31,9 +31,9 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
return nil
})
userGroupResp := []*m.UserGroup{}
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
query.Result = userGroupResp
teamResp := []*m.Team{}
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
query.Result = teamResp
return nil
})
......@@ -81,9 +81,9 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
})
})
Convey("When user is a member of a user group in the ACL with admin permission", func() {
Convey("When user is a member of a team in the ACL with admin permission", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardsId/acl/:aclId", m.ROLE_EDITOR, func(sc *scenarioContext) {
userGroupResp = append(userGroupResp, &m.UserGroup{Id: 2, OrgId: 1, Name: "UG2"})
teamResp = append(teamResp, &m.Team{Id: 2, OrgId: 1, Name: "UG2"})
bus.AddHandler("test3", func(cmd *m.RemoveDashboardAclCommand) error {
return nil
......@@ -165,7 +165,7 @@ func transformDashboardAclsToDTOs(acls []*m.DashboardAclInfoDTO) []*m.DashboardA
DashboardId: acl.DashboardId,
Permission: acl.Permission,
UserId: acl.UserId,
UserGroupId: acl.UserGroupId,
TeamId: acl.TeamId,
}
dtos = append(dtos, dto)
}
......
......@@ -56,8 +56,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
})
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
query.Result = []*m.UserGroup{}
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
query.Result = []*m.Team{}
return nil
})
......@@ -217,8 +217,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
})
bus.AddHandler("test", func(query *m.GetUserGroupsByUserQuery) error {
query.Result = []*m.UserGroup{}
bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
query.Result = []*m.Team{}
return nil
})
......
......@@ -10,7 +10,7 @@ type UpdateDashboardAclCommand struct {
type DashboardAclUpdateItem struct {
UserId int64 `json:"userId"`
UserGroupId int64 `json:"userGroupId"`
TeamId int64 `json:"teamId"`
Role *m.RoleType `json:"role,omitempty"`
Permission m.PermissionType `json:"permission"`
}
......@@ -231,8 +231,8 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Text: "Teams",
Id: "teams",
Description: "Manage org groups",
Icon: "gicon gicon-user-group",
Url: setting.AppSubUrl + "/org/user-groups",
Icon: "gicon gicon-team",
Url: setting.AppSubUrl + "/org/teams",
},
{
Text: "Plugins",
......
......@@ -7,48 +7,48 @@ import (
"github.com/grafana/grafana/pkg/util"
)
// POST /api/user-groups
func CreateUserGroup(c *middleware.Context, cmd m.CreateUserGroupCommand) Response {
// POST /api/teams
func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrUserGroupNameTaken {
return ApiError(409, "User Group name taken", err)
if err == m.ErrTeamNameTaken {
return ApiError(409, "Team name taken", err)
}
return ApiError(500, "Failed to create User Group", err)
return ApiError(500, "Failed to create Team", err)
}
return Json(200, &util.DynMap{
"userGroupId": cmd.Result.Id,
"message": "User Group created",
"teamId": cmd.Result.Id,
"message": "Team created",
})
}
// PUT /api/user-groups/:userGroupId
func UpdateUserGroup(c *middleware.Context, cmd m.UpdateUserGroupCommand) Response {
cmd.Id = c.ParamsInt64(":userGroupId")
// PUT /api/teams/:teamId
func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
cmd.Id = c.ParamsInt64(":teamId")
if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrUserGroupNameTaken {
return ApiError(400, "User Group name taken", err)
if err == m.ErrTeamNameTaken {
return ApiError(400, "Team name taken", err)
}
return ApiError(500, "Failed to update User Group", err)
return ApiError(500, "Failed to update Team", err)
}
return ApiSuccess("User Group updated")
return ApiSuccess("Team updated")
}
// DELETE /api/user-groups/:userGroupId
func DeleteUserGroupById(c *middleware.Context) Response {
if err := bus.Dispatch(&m.DeleteUserGroupCommand{Id: c.ParamsInt64(":userGroupId")}); err != nil {
if err == m.ErrUserGroupNotFound {
return ApiError(404, "Failed to delete User Group. ID not found", nil)
// DELETE /api/teams/:teamId
func DeleteTeamById(c *middleware.Context) Response {
if err := bus.Dispatch(&m.DeleteTeamCommand{Id: c.ParamsInt64(":teamId")}); err != nil {
if err == m.ErrTeamNotFound {
return ApiError(404, "Failed to delete Team. ID not found", nil)
}
return ApiError(500, "Failed to update User Group", err)
return ApiError(500, "Failed to update Team", err)
}
return ApiSuccess("User Group deleted")
return ApiSuccess("Team deleted")
}
// GET /api/user-groups/search
func SearchUserGroups(c *middleware.Context) Response {
// GET /api/teams/search
func SearchTeams(c *middleware.Context) Response {
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
......@@ -58,7 +58,7 @@ func SearchUserGroups(c *middleware.Context) Response {
page = 1
}
query := m.SearchUserGroupsQuery{
query := m.SearchTeamsQuery{
Query: c.Query("query"),
Name: c.Query("name"),
Page: page,
......@@ -67,7 +67,7 @@ func SearchUserGroups(c *middleware.Context) Response {
}
if err := bus.Dispatch(&query); err != nil {
return ApiError(500, "Failed to search User Groups", err)
return ApiError(500, "Failed to search Teams", err)
}
query.Result.Page = page
......@@ -76,16 +76,16 @@ func SearchUserGroups(c *middleware.Context) Response {
return Json(200, query.Result)
}
// GET /api/user-groups/:userGroupId
func GetUserGroupById(c *middleware.Context) Response {
query := m.GetUserGroupByIdQuery{Id: c.ParamsInt64(":userGroupId")}
// GET /api/teams/:teamId
func GetTeamById(c *middleware.Context) Response {
query := m.GetTeamByIdQuery{Id: c.ParamsInt64(":teamId")}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrUserGroupNotFound {
return ApiError(404, "User Group not found", err)
if err == m.ErrTeamNotFound {
return ApiError(404, "Team not found", err)
}
return ApiError(500, "Failed to get User Group", err)
return ApiError(500, "Failed to get Team", err)
}
return Json(200, &query.Result)
......
......@@ -7,38 +7,38 @@ import (
"github.com/grafana/grafana/pkg/util"
)
// GET /api/user-groups/:userGroupId/members
func GetUserGroupMembers(c *middleware.Context) Response {
query := m.GetUserGroupMembersQuery{UserGroupId: c.ParamsInt64(":userGroupId")}
// GET /api/teams/:teamId/members
func GetTeamMembers(c *middleware.Context) Response {
query := m.GetTeamMembersQuery{TeamId: c.ParamsInt64(":teamId")}
if err := bus.Dispatch(&query); err != nil {
return ApiError(500, "Failed to get User Group Members", err)
return ApiError(500, "Failed to get Team Members", err)
}
return Json(200, query.Result)
}
// POST /api/user-groups/:userGroupId/members
func AddUserGroupMember(c *middleware.Context, cmd m.AddUserGroupMemberCommand) Response {
cmd.UserGroupId = c.ParamsInt64(":userGroupId")
// POST /api/teams/:teamId/members
func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
cmd.TeamId = c.ParamsInt64(":teamId")
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrUserGroupMemberAlreadyAdded {
return ApiError(400, "User is already added to this user group", err)
if err == m.ErrTeamMemberAlreadyAdded {
return ApiError(400, "User is already added to this team", err)
}
return ApiError(500, "Failed to add Member to User Group", err)
return ApiError(500, "Failed to add Member to Team", err)
}
return Json(200, &util.DynMap{
"message": "Member added to User Group",
"message": "Member added to Team",
})
}
// DELETE /api/user-groups/:userGroupId/members/:userId
func RemoveUserGroupMember(c *middleware.Context) Response {
if err := bus.Dispatch(&m.RemoveUserGroupMemberCommand{UserGroupId: c.ParamsInt64(":userGroupId"), UserId: c.ParamsInt64(":userId")}); err != nil {
return ApiError(500, "Failed to remove Member from User Group", err)
// DELETE /api/teams/:teamId/members/:userId
func RemoveTeamMember(c *middleware.Context) Response {
if err := bus.Dispatch(&m.RemoveTeamMemberCommand{TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil {
return ApiError(500, "Failed to remove Member from Team", err)
}
return ApiSuccess("User Group Member removed")
return ApiSuccess("Team Member removed")
}
......@@ -10,21 +10,21 @@ import (
. "github.com/smartystreets/goconvey/convey"
)
func TestUserGroupApiEndpoint(t *testing.T) {
Convey("Given two user groups", t, func() {
mockResult := models.SearchUserGroupQueryResult{
UserGroups: []*models.UserGroup{
{Name: "userGroup1"},
{Name: "userGroup2"},
func TestTeamApiEndpoint(t *testing.T) {
Convey("Given two teams", t, func() {
mockResult := models.SearchTeamQueryResult{
Teams: []*models.Team{
{Name: "team1"},
{Name: "team2"},
},
TotalCount: 2,
}
Convey("When searching with no parameters", func() {
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) {
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error {
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
sentLimit = query.Limit
......@@ -33,7 +33,7 @@ func TestUserGroupApiEndpoint(t *testing.T) {
return nil
})
sc.handlerFunc = SearchUserGroups
sc.handlerFunc = SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sentLimit, ShouldEqual, 1000)
......@@ -43,15 +43,15 @@ func TestUserGroupApiEndpoint(t *testing.T) {
So(err, ShouldBeNil)
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2)
So(len(respJSON.Get("userGroups").MustArray()), ShouldEqual, 2)
So(len(respJSON.Get("teams").MustArray()), ShouldEqual, 2)
})
})
Convey("When searching with page and perpage parameters", func() {
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) {
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error {
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
sentLimit = query.Limit
......@@ -60,7 +60,7 @@ func TestUserGroupApiEndpoint(t *testing.T) {
return nil
})
sc.handlerFunc = SearchUserGroups
sc.handlerFunc = SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
So(sentLimit, ShouldEqual, 10)
......
......@@ -24,7 +24,7 @@ func (p PermissionType) String() string {
// Typed errors
var (
ErrDashboardAclInfoMissing = errors.New("User id and user group id cannot both be empty for a dashboard permission.")
ErrDashboardAclInfoMissing = errors.New("User id and team id cannot both be empty for a dashboard permission.")
ErrDashboardPermissionDashboardEmpty = errors.New("Dashboard Id must be greater than zero for a dashboard permission.")
)
......@@ -35,7 +35,7 @@ type DashboardAcl struct {
DashboardId int64
UserId int64
UserGroupId int64
TeamId int64
Role *RoleType // pointer to be nullable
Permission PermissionType
......@@ -54,8 +54,8 @@ type DashboardAclInfoDTO struct {
UserId int64 `json:"userId"`
UserLogin string `json:"userLogin"`
UserEmail string `json:"userEmail"`
UserGroupId int64 `json:"userGroupId"`
UserGroup string `json:"userGroup"`
TeamId int64 `json:"teamId"`
Team string `json:"team"`
Role *RoleType `json:"role,omitempty"`
Permission PermissionType `json:"permission"`
PermissionName string `json:"permissionName"`
......@@ -74,7 +74,7 @@ type SetDashboardAclCommand struct {
DashboardId int64
OrgId int64
UserId int64
UserGroupId int64
TeamId int64
Permission PermissionType
Result DashboardAcl
......
......@@ -7,12 +7,12 @@ import (
// Typed errors
var (
ErrUserGroupNotFound = errors.New("User Group not found")
ErrUserGroupNameTaken = errors.New("User Group name is taken")
ErrTeamNotFound = errors.New("Team not found")
ErrTeamNameTaken = errors.New("Team name is taken")
)
// UserGroup model
type UserGroup struct {
// Team model
type Team struct {
Id int64 `json:"id"`
OrgId int64 `json:"orgId"`
Name string `json:"name"`
......@@ -24,45 +24,45 @@ type UserGroup struct {
// ---------------------
// COMMANDS
type CreateUserGroupCommand struct {
type CreateTeamCommand struct {
Name string `json:"name" binding:"Required"`
OrgId int64 `json:"-"`
Result UserGroup `json:"-"`
Result Team `json:"-"`
}
type UpdateUserGroupCommand struct {
type UpdateTeamCommand struct {
Id int64
Name string
}
type DeleteUserGroupCommand struct {
type DeleteTeamCommand struct {
Id int64
}
type GetUserGroupByIdQuery struct {
type GetTeamByIdQuery struct {
Id int64
Result *UserGroup
Result *Team
}
type GetUserGroupsByUserQuery struct {
type GetTeamsByUserQuery struct {
UserId int64 `json:"userId"`
Result []*UserGroup `json:"userGroups"`
Result []*Team `json:"teams"`
}
type SearchUserGroupsQuery struct {
type SearchTeamsQuery struct {
Query string
Name string
Limit int
Page int
OrgId int64
Result SearchUserGroupQueryResult
Result SearchTeamQueryResult
}
type SearchUserGroupQueryResult struct {
type SearchTeamQueryResult struct {
TotalCount int64 `json:"totalCount"`
UserGroups []*UserGroup `json:"userGroups"`
Teams []*Team `json:"teams"`
Page int `json:"page"`
PerPage int `json:"perPage"`
}
package models
import (
"errors"
"time"
)
// Typed errors
var (
ErrTeamMemberAlreadyAdded = errors.New("User is already added to this team")
)
// TeamMember model
type TeamMember struct {
Id int64
OrgId int64
TeamId int64
UserId int64
Created time.Time
Updated time.Time
}
// ---------------------
// COMMANDS
type AddTeamMemberCommand struct {
UserId int64 `json:"userId" binding:"Required"`
OrgId int64 `json:"-"`
TeamId int64 `json:"-"`
}
type RemoveTeamMemberCommand struct {
UserId int64
TeamId int64
}
// ----------------------
// QUERIES
type GetTeamMembersQuery struct {
TeamId int64
Result []*TeamMemberDTO
}
// ----------------------
// Projections and DTOs
type TeamMemberDTO struct {
OrgId int64 `json:"orgId"`
TeamId int64 `json:"teamId"`
UserId int64 `json:"userId"`
Email string `json:"email"`
Login string `json:"login"`
}
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 {
UserId int64 `json:"userId" binding:"Required"`
OrgId int64 `json:"-"`
UserGroupId 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:"userGroupId"`
UserId int64 `json:"userId"`
Email string `json:"email"`
Login string `json:"login"`
}
......@@ -11,7 +11,7 @@ type DashboardGuardian struct {
dashId int64
orgId int64
acl []*m.DashboardAclInfoDTO
groups []*m.UserGroup
groups []*m.Team
log log.Logger
}
......@@ -55,7 +55,7 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
orgRole = m.ROLE_VIEWER
}
userGroupAclItems := []*m.DashboardAclInfoDTO{}
teamAclItems := []*m.DashboardAclInfoDTO{}
for _, p := range acl {
// user match
......@@ -71,26 +71,26 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
}
// remember this rule for later
if p.UserGroupId > 0 {
userGroupAclItems = append(userGroupAclItems, p)
if p.TeamId > 0 {
teamAclItems = append(teamAclItems, p)
}
}
// do we have group rules?
if len(userGroupAclItems) == 0 {
if len(teamAclItems) == 0 {
return false, nil
}
// load groups
userGroups, err := g.getUserGroups()
teams, err := g.getTeams()
if err != nil {
return false, err
}
// evalute group rules
for _, p := range acl {
for _, ug := range userGroups {
if ug.Id == p.UserGroupId && p.Permission >= permission {
for _, ug := range teams {
if ug.Id == p.TeamId && p.Permission >= permission {
return true, nil
}
}
......@@ -114,12 +114,12 @@ func (g *DashboardGuardian) GetAcl() ([]*m.DashboardAclInfoDTO, error) {
return g.acl, nil
}
func (g *DashboardGuardian) getUserGroups() ([]*m.UserGroup, error) {
func (g *DashboardGuardian) getTeams() ([]*m.Team, error) {
if g.groups != nil {
return g.groups, nil
}
query := m.GetUserGroupsByUserQuery{UserId: g.user.UserId}
query := m.GetTeamsByUserQuery{UserId: g.user.UserId}
err := bus.Dispatch(&query)
g.groups = query.Result
......
......@@ -24,7 +24,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
}
for _, item := range cmd.Items {
if item.UserId == 0 && item.UserGroupId == 0 && !item.Role.IsValid() {
if item.UserId == 0 && item.TeamId == 0 && !item.Role.IsValid() {
return m.ErrDashboardAclInfoMissing
}
......@@ -32,7 +32,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
return m.ErrDashboardPermissionDashboardEmpty
}
sess.Nullable("user_id", "user_group_id")
sess.Nullable("user_id", "team_id")
if _, err := sess.Insert(item); err != nil {
return err
}
......@@ -49,7 +49,7 @@ func UpdateDashboardAcl(cmd *m.UpdateDashboardAclCommand) error {
func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
return inTransaction(func(sess *DBSession) error {
if cmd.UserId == 0 && cmd.UserGroupId == 0 {
if cmd.UserId == 0 && cmd.TeamId == 0 {
return m.ErrDashboardAclInfoMissing
}
......@@ -57,7 +57,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
return m.ErrDashboardPermissionDashboardEmpty
}
if res, err := sess.Query("SELECT 1 from "+dialect.Quote("dashboard_acl")+" WHERE dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId); err != nil {
if res, err := sess.Query("SELECT 1 from "+dialect.Quote("dashboard_acl")+" WHERE dashboard_id =? and (team_id=? or user_id=?)", cmd.DashboardId, cmd.TeamId, cmd.UserId); err != nil {
return err
} else if len(res) == 1 {
......@@ -66,7 +66,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
Updated: time.Now(),
}
if _, err := sess.Cols("updated", "permission").Where("dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId).Update(&entity); err != nil {
if _, err := sess.Cols("updated", "permission").Where("dashboard_id =? and (team_id=? or user_id=?)", cmd.DashboardId, cmd.TeamId, cmd.UserId).Update(&entity); err != nil {
return err
}
......@@ -75,7 +75,7 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
entity := m.DashboardAcl{
OrgId: cmd.OrgId,
UserGroupId: cmd.UserGroupId,
TeamId: cmd.TeamId,
UserId: cmd.UserId,
Created: time.Now(),
Updated: time.Now(),
......@@ -89,8 +89,8 @@ func SetDashboardAcl(cmd *m.SetDashboardAclCommand) error {
cols = append(cols, "user_id")
}
if cmd.UserGroupId != 0 {
cols = append(cols, "user_group_id")
if cmd.TeamId != 0 {
cols = append(cols, "team_id")
}
_, err := sess.Cols(cols...).Insert(&entity)
......@@ -138,17 +138,17 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
da.org_id,
da.dashboard_id,
da.user_id,
da.user_group_id,
da.team_id,
da.permission,
da.role,
da.created,
da.updated,
u.login AS user_login,
u.email AS user_email,
ug.name AS user_group
ug.name AS team
FROM` + dialect.Quote("dashboard_acl") + ` as da
LEFT OUTER JOIN ` + dialect.Quote("user") + ` AS u ON u.id = da.user_id
LEFT OUTER JOIN user_group ug on ug.id = da.user_group_id
LEFT OUTER JOIN team ug on ug.id = da.team_id
WHERE dashboard_id ` + dashboardFilter + ` AND da.org_id = ?
-- Also include default permission if has_acl = 0
......@@ -159,14 +159,14 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
da.org_id,
da.dashboard_id,
da.user_id,
da.user_group_id,
da.team_id,
da.permission,
da.role,
da.created,
da.updated,
'' as user_login,
'' as user_email,
'' as user_group
'' as team
FROM dashboard_acl as da,
dashboard as dash
LEFT JOIN dashboard folder on dash.folder_id = folder.id
......
......@@ -16,7 +16,7 @@ func TestDashboardAclDataAccess(t *testing.T) {
savedFolder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
childDash := insertTestDashboard("2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
Convey("When adding dashboard permission with userId and userGroupId set to 0", func() {
Convey("When adding dashboard permission with userId and teamId set to 0", func() {
err := SetDashboardAcl(&m.SetDashboardAclCommand{
OrgId: 1,
DashboardId: savedFolder.Id,
......@@ -175,15 +175,15 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
})
Convey("Given a user group", func() {
group1 := m.CreateUserGroupCommand{Name: "group1 name", OrgId: 1}
err := CreateUserGroup(&group1)
Convey("Given a team", func() {
group1 := m.CreateTeamCommand{Name: "group1 name", OrgId: 1}
err := CreateTeam(&group1)
So(err, ShouldBeNil)
Convey("Should be able to add a user permission for a user group", func() {
Convey("Should be able to add a user permission for a team", func() {
setDashAclCmd := m.SetDashboardAclCommand{
OrgId: 1,
UserGroupId: group1.Result.Id,
TeamId: group1.Result.Id,
DashboardId: savedFolder.Id,
Permission: m.PERMISSION_EDIT,
}
......@@ -196,9 +196,9 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(err, ShouldBeNil)
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(q1.Result[0].Permission, ShouldEqual, m.PERMISSION_EDIT)
So(q1.Result[0].UserGroupId, ShouldEqual, group1.Result.Id)
So(q1.Result[0].TeamId, ShouldEqual, group1.Result.Id)
Convey("Should be able to delete an existing permission for a user group", func() {
Convey("Should be able to delete an existing permission for a team", func() {
err := RemoveDashboardAcl(&m.RemoveDashboardAclCommand{
OrgId: 1,
AclId: setDashAclCmd.Result.Id,
......@@ -212,10 +212,10 @@ func TestDashboardAclDataAccess(t *testing.T) {
})
})
Convey("Should be able to update an existing permission for a user group", func() {
Convey("Should be able to update an existing permission for a team", func() {
err := SetDashboardAcl(&m.SetDashboardAclCommand{
OrgId: 1,
UserGroupId: group1.Result.Id,
TeamId: group1.Result.Id,
DashboardId: savedFolder.Id,
Permission: m.PERMISSION_ADMIN,
})
......@@ -227,7 +227,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, m.PERMISSION_ADMIN)
So(q3.Result[0].UserGroupId, ShouldEqual, group1.Result.Id)
So(q3.Result[0].TeamId, ShouldEqual, group1.Result.Id)
})
})
......
......@@ -10,7 +10,7 @@ func addDashboardAclMigrations(mg *Migrator) {
{Name: "org_id", Type: DB_BigInt},
{Name: "dashboard_id", Type: DB_BigInt},
{Name: "user_id", Type: DB_BigInt, Nullable: true},
{Name: "user_group_id", Type: DB_BigInt, Nullable: true},
{Name: "team_id", Type: DB_BigInt, Nullable: true},
{Name: "permission", Type: DB_SmallInt, Default: "4"},
{Name: "role", Type: DB_Varchar, Length: 20, Nullable: true},
{Name: "created", Type: DB_DateTime, Nullable: false},
......@@ -19,7 +19,7 @@ func addDashboardAclMigrations(mg *Migrator) {
Indices: []*Index{
{Cols: []string{"dashboard_id"}},
{Cols: []string{"dashboard_id", "user_id"}, Type: UniqueIndex},
{Cols: []string{"dashboard_id", "user_group_id"}, Type: UniqueIndex},
{Cols: []string{"dashboard_id", "team_id"}, Type: UniqueIndex},
},
}
......
......@@ -26,7 +26,7 @@ func AddMigrations(mg *Migrator) {
addAnnotationMig(mg)
addTestDataMigrations(mg)
addDashboardVersionMigration(mg)
addUserGroupMigrations(mg)
addTeamMigrations(mg)
addDashboardAclMigrations(mg)
addTagMigration(mg)
}
......
......@@ -2,9 +2,9 @@ package migrations
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
func addUserGroupMigrations(mg *Migrator) {
userGroupV1 := Table{
Name: "user_group",
func addTeamMigrations(mg *Migrator) {
teamV1 := Table{
Name: "team",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
......@@ -18,31 +18,31 @@ func addUserGroupMigrations(mg *Migrator) {
},
}
mg.AddMigration("create user group table", NewAddTableMigration(userGroupV1))
mg.AddMigration("create team table", NewAddTableMigration(teamV1))
//------- indexes ------------------
mg.AddMigration("add index user_group.org_id", NewAddIndexMigration(userGroupV1, userGroupV1.Indices[0]))
mg.AddMigration("add unique index user_group_org_id_name", NewAddIndexMigration(userGroupV1, userGroupV1.Indices[1]))
mg.AddMigration("add index team.org_id", NewAddIndexMigration(teamV1, teamV1.Indices[0]))
mg.AddMigration("add unique index team_org_id_name", NewAddIndexMigration(teamV1, teamV1.Indices[1]))
userGroupMemberV1 := Table{
Name: "user_group_member",
teamMemberV1 := Table{
Name: "team_member",
Columns: []*Column{
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
{Name: "org_id", Type: DB_BigInt},
{Name: "user_group_id", Type: DB_BigInt},
{Name: "team_id", Type: DB_BigInt},
{Name: "user_id", Type: DB_BigInt},
{Name: "created", Type: DB_DateTime, Nullable: false},
{Name: "updated", Type: DB_DateTime, Nullable: false},
},
Indices: []*Index{
{Cols: []string{"org_id"}},
{Cols: []string{"org_id", "user_group_id", "user_id"}, Type: UniqueIndex},
{Cols: []string{"org_id", "team_id", "user_id"}, Type: UniqueIndex},
},
}
mg.AddMigration("create user group member table", NewAddTableMigration(userGroupMemberV1))
mg.AddMigration("create team member table", NewAddTableMigration(teamMemberV1))
//------- indexes ------------------
mg.AddMigration("add index user_group_member.org_id", NewAddIndexMigration(userGroupMemberV1, userGroupMemberV1.Indices[0]))
mg.AddMigration("add unique index user_group_member_org_id_user_group_id_user_id", NewAddIndexMigration(userGroupMemberV1, userGroupMemberV1.Indices[1]))
mg.AddMigration("add index team_member.org_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[0]))
mg.AddMigration("add unique index team_member_org_id_team_id_user_id", NewAddIndexMigration(teamMemberV1, teamMemberV1.Indices[1]))
}
......@@ -91,7 +91,7 @@ func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
deletes := []string{
"DELETE FROM org_user WHERE org_id=? and user_id=?",
"DELETE FROM dashboard_acl WHERE org_id=? and user_id = ?",
"DELETE FROM user_group_member WHERE org_id=? and user_id = ?",
"DELETE FROM team_member WHERE org_id=? and user_id = ?",
}
for _, sql := range deletes {
......
......@@ -179,7 +179,7 @@ func (sb *SearchBuilder) buildSearchWhereClause() {
SELECT distinct d.id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
LEFT JOIN user_group_member as ugm on ugm.user_group_id = da.user_group_id
LEFT JOIN team_member as ugm on ugm.team_id = da.team_id
LEFT JOIN org_user ou on ou.role = da.role
WHERE
d.has_acl = 1 and
......
......@@ -9,82 +9,82 @@ import (
)
func init() {
bus.AddHandler("sql", CreateUserGroup)
bus.AddHandler("sql", UpdateUserGroup)
bus.AddHandler("sql", DeleteUserGroup)
bus.AddHandler("sql", SearchUserGroups)
bus.AddHandler("sql", GetUserGroupById)
bus.AddHandler("sql", GetUserGroupsByUser)
bus.AddHandler("sql", AddUserGroupMember)
bus.AddHandler("sql", RemoveUserGroupMember)
bus.AddHandler("sql", GetUserGroupMembers)
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", RemoveTeamMember)
bus.AddHandler("sql", GetTeamMembers)
}
func CreateUserGroup(cmd *m.CreateUserGroupCommand) error {
func CreateTeam(cmd *m.CreateTeamCommand) error {
return inTransaction(func(sess *DBSession) error {
if isNameTaken, err := isUserGroupNameTaken(cmd.Name, 0, sess); err != nil {
if isNameTaken, err := isTeamNameTaken(cmd.Name, 0, sess); err != nil {
return err
} else if isNameTaken {
return m.ErrUserGroupNameTaken
return m.ErrTeamNameTaken
}
userGroup := m.UserGroup{
team := m.Team{
Name: cmd.Name,
OrgId: cmd.OrgId,
Created: time.Now(),
Updated: time.Now(),
}
_, err := sess.Insert(&userGroup)
_, err := sess.Insert(&team)
cmd.Result = userGroup
cmd.Result = team
return err
})
}
func UpdateUserGroup(cmd *m.UpdateUserGroupCommand) error {
func UpdateTeam(cmd *m.UpdateTeamCommand) error {
return inTransaction(func(sess *DBSession) error {
if isNameTaken, err := isUserGroupNameTaken(cmd.Name, cmd.Id, sess); err != nil {
if isNameTaken, err := isTeamNameTaken(cmd.Name, cmd.Id, sess); err != nil {
return err
} else if isNameTaken {
return m.ErrUserGroupNameTaken
return m.ErrTeamNameTaken
}
userGroup := m.UserGroup{
team := m.Team{
Name: cmd.Name,
Updated: time.Now(),
}
affectedRows, err := sess.Id(cmd.Id).Update(&userGroup)
affectedRows, err := sess.Id(cmd.Id).Update(&team)
if err != nil {
return err
}
if affectedRows == 0 {
return m.ErrUserGroupNotFound
return m.ErrTeamNotFound
}
return nil
})
}
func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error {
func DeleteTeam(cmd *m.DeleteTeamCommand) error {
return inTransaction(func(sess *DBSession) error {
if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.Id); err != nil {
if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.Id); err != nil {
return err
} else if len(res) != 1 {
return m.ErrUserGroupNotFound
return m.ErrTeamNotFound
}
deletes := []string{
"DELETE FROM user_group_member WHERE user_group_id = ?",
"DELETE FROM user_group WHERE id = ?",
"DELETE FROM dashboard_acl WHERE user_group_id = ?",
"DELETE FROM team_member WHERE team_id = ?",
"DELETE FROM team WHERE id = ?",
"DELETE FROM dashboard_acl WHERE team_id = ?",
}
for _, sql := range deletes {
......@@ -97,28 +97,28 @@ func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error {
})
}
func isUserGroupNameTaken(name string, existingId int64, sess *DBSession) (bool, error) {
var userGroup m.UserGroup
exists, err := sess.Where("name=?", name).Get(&userGroup)
func isTeamNameTaken(name string, existingId int64, sess *DBSession) (bool, error) {
var team m.Team
exists, err := sess.Where("name=?", name).Get(&team)
if err != nil {
return false, nil
}
if exists && existingId != userGroup.Id {
if exists && existingId != team.Id {
return true, nil
}
return false, nil
}
func SearchUserGroups(query *m.SearchUserGroupsQuery) error {
query.Result = m.SearchUserGroupQueryResult{
UserGroups: make([]*m.UserGroup, 0),
func SearchTeams(query *m.SearchTeamsQuery) error {
query.Result = m.SearchTeamQueryResult{
Teams: make([]*m.Team, 0),
}
queryWithWildcards := "%" + query.Query + "%"
sess := x.Table("user_group")
sess := x.Table("team")
sess.Where("org_id=?", query.OrgId)
if query.Query != "" {
......@@ -132,46 +132,46 @@ func SearchUserGroups(query *m.SearchUserGroupsQuery) error {
offset := query.Limit * (query.Page - 1)
sess.Limit(query.Limit, offset)
sess.Cols("id", "name")
if err := sess.Find(&query.Result.UserGroups); err != nil {
if err := sess.Find(&query.Result.Teams); err != nil {
return err
}
userGroup := m.UserGroup{}
team := m.Team{}
countSess := x.Table("user_group")
countSess := x.Table("team")
if query.Query != "" {
countSess.Where("name LIKE ?", queryWithWildcards)
}
if query.Name != "" {
countSess.Where("name=?", query.Name)
}
count, err := countSess.Count(&userGroup)
count, err := countSess.Count(&team)
query.Result.TotalCount = count
return err
}
func GetUserGroupById(query *m.GetUserGroupByIdQuery) error {
var userGroup m.UserGroup
exists, err := x.Id(query.Id).Get(&userGroup)
func GetTeamById(query *m.GetTeamByIdQuery) error {
var team m.Team
exists, err := x.Id(query.Id).Get(&team)
if err != nil {
return err
}
if !exists {
return m.ErrUserGroupNotFound
return m.ErrTeamNotFound
}
query.Result = &userGroup
query.Result = &team
return nil
}
func GetUserGroupsByUser(query *m.GetUserGroupsByUserQuery) error {
query.Result = make([]*m.UserGroup, 0)
func GetTeamsByUser(query *m.GetTeamsByUserQuery) error {
query.Result = make([]*m.Team, 0)
sess := x.Table("user_group")
sess.Join("INNER", "user_group_member", "user_group.id=user_group_member.user_group_id")
sess.Where("user_group_member.user_id=?", query.UserId)
sess := x.Table("team")
sess.Join("INNER", "team_member", "team.id=team_member.team_id")
sess.Where("team_member.user_id=?", query.UserId)
err := sess.Find(&query.Result)
if err != nil {
......@@ -181,23 +181,23 @@ func GetUserGroupsByUser(query *m.GetUserGroupsByUserQuery) error {
return nil
}
func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) error {
func AddTeamMember(cmd *m.AddTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) 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 {
if res, err := sess.Query("SELECT 1 from team_member WHERE team_id=? and user_id=?", cmd.TeamId, cmd.UserId); err != nil {
return err
} else if len(res) == 1 {
return m.ErrUserGroupMemberAlreadyAdded
return m.ErrTeamMemberAlreadyAdded
}
if res, err := sess.Query("SELECT 1 from user_group WHERE id=?", cmd.UserGroupId); err != nil {
if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.TeamId); err != nil {
return err
} else if len(res) != 1 {
return m.ErrUserGroupNotFound
return m.ErrTeamNotFound
}
entity := m.UserGroupMember{
entity := m.TeamMember{
OrgId: cmd.OrgId,
UserGroupId: cmd.UserGroupId,
TeamId: cmd.TeamId,
UserId: cmd.UserId,
Created: time.Now(),
Updated: time.Now(),
......@@ -208,10 +208,10 @@ func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) error {
})
}
func RemoveUserGroupMember(cmd *m.RemoveUserGroupMemberCommand) error {
func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
return inTransaction(func(sess *DBSession) error {
var rawSql = "DELETE FROM user_group_member WHERE user_group_id=? and user_id=?"
_, err := sess.Exec(rawSql, cmd.UserGroupId, cmd.UserId)
var rawSql = "DELETE FROM team_member WHERE team_id=? and user_id=?"
_, err := sess.Exec(rawSql, cmd.TeamId, cmd.UserId)
if err != nil {
return err
}
......@@ -220,12 +220,12 @@ func RemoveUserGroupMember(cmd *m.RemoveUserGroupMemberCommand) error {
})
}
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")
func GetTeamMembers(query *m.GetTeamMembersQuery) error {
query.Result = make([]*m.TeamMemberDTO, 0)
sess := x.Table("team_member")
sess.Join("INNER", "user", fmt.Sprintf("team_member.user_id=%s.id", x.Dialect().Quote("user")))
sess.Where("team_member.team_id=?", query.TeamId)
sess.Cols("user.org_id", "team_member.team_id", "team_member.user_id", "user.email", "user.login")
sess.Asc("user.login", "user.email")
err := sess.Find(&query.Result)
......
......@@ -9,12 +9,12 @@ import (
m "github.com/grafana/grafana/pkg/models"
)
func TestUserGroupCommandsAndQueries(t *testing.T) {
func TestTeamCommandsAndQueries(t *testing.T) {
Convey("Testing User Group commands & queries", t, func() {
Convey("Testing Team commands & queries", t, func() {
InitTestDB(t)
Convey("Given saved users and two user groups", func() {
Convey("Given saved users and two teams", func() {
var userIds []int64
for i := 0; i < 5; i++ {
userCmd := &m.CreateUserCommand{
......@@ -27,81 +27,81 @@ func TestUserGroupCommandsAndQueries(t *testing.T) {
userIds = append(userIds, userCmd.Result.Id)
}
group1 := m.CreateUserGroupCommand{Name: "group1 name"}
group2 := m.CreateUserGroupCommand{Name: "group2 name"}
group1 := m.CreateTeamCommand{Name: "group1 name"}
group2 := m.CreateTeamCommand{Name: "group2 name"}
err := CreateUserGroup(&group1)
err := CreateTeam(&group1)
So(err, ShouldBeNil)
err = CreateUserGroup(&group2)
err = CreateTeam(&group2)
So(err, ShouldBeNil)
Convey("Should be able to create user groups and add users", func() {
query := &m.SearchUserGroupsQuery{Name: "group1 name", Page: 1, Limit: 10}
err = SearchUserGroups(query)
Convey("Should be able to create teams and add users", func() {
query := &m.SearchTeamsQuery{Name: "group1 name", Page: 1, Limit: 10}
err = SearchTeams(query)
So(err, ShouldBeNil)
So(query.Page, ShouldEqual, 1)
userGroup1 := query.Result.UserGroups[0]
So(userGroup1.Name, ShouldEqual, "group1 name")
team1 := query.Result.Teams[0]
So(team1.Name, ShouldEqual, "group1 name")
err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: userGroup1.Id, UserId: userIds[0]})
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: team1.Id, UserId: userIds[0]})
So(err, ShouldBeNil)
q1 := &m.GetUserGroupMembersQuery{UserGroupId: userGroup1.Id}
err = GetUserGroupMembers(q1)
q1 := &m.GetTeamMembersQuery{TeamId: team1.Id}
err = GetTeamMembers(q1)
So(err, ShouldBeNil)
So(q1.Result[0].UserGroupId, ShouldEqual, userGroup1.Id)
So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
So(q1.Result[0].Login, ShouldEqual, "loginuser0")
})
Convey("Should be able to search for user groups", func() {
query := &m.SearchUserGroupsQuery{Query: "group", Page: 1}
err = SearchUserGroups(query)
Convey("Should be able to search for teams", func() {
query := &m.SearchTeamsQuery{Query: "group", Page: 1}
err = SearchTeams(query)
So(err, ShouldBeNil)
So(len(query.Result.UserGroups), ShouldEqual, 2)
So(len(query.Result.Teams), ShouldEqual, 2)
So(query.Result.TotalCount, ShouldEqual, 2)
query2 := &m.SearchUserGroupsQuery{Query: ""}
err = SearchUserGroups(query2)
query2 := &m.SearchTeamsQuery{Query: ""}
err = SearchTeams(query2)
So(err, ShouldBeNil)
So(len(query2.Result.UserGroups), ShouldEqual, 2)
So(len(query2.Result.Teams), ShouldEqual, 2)
})
Convey("Should be able to return all user groups a user is member of", func() {
Convey("Should be able to return all teams a user is member of", func() {
groupId := group2.Result.Id
err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[0]})
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[0]})
query := &m.GetUserGroupsByUserQuery{UserId: userIds[0]}
err = GetUserGroupsByUser(query)
query := &m.GetTeamsByUserQuery{UserId: userIds[0]}
err = GetTeamsByUser(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
So(query.Result[0].Name, ShouldEqual, "group2 name")
})
Convey("Should be able to remove users from a group", func() {
err = RemoveUserGroupMember(&m.RemoveUserGroupMemberCommand{UserGroupId: group1.Result.Id, UserId: userIds[0]})
err = RemoveTeamMember(&m.RemoveTeamMemberCommand{TeamId: group1.Result.Id, UserId: userIds[0]})
So(err, ShouldBeNil)
q1 := &m.GetUserGroupMembersQuery{UserGroupId: group1.Result.Id}
err = GetUserGroupMembers(q1)
q1 := &m.GetTeamMembersQuery{TeamId: group1.Result.Id}
err = GetTeamMembers(q1)
So(err, ShouldBeNil)
So(len(q1.Result), ShouldEqual, 0)
})
Convey("Should be able to remove a group with users and permissions", func() {
groupId := group2.Result.Id
err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[1]})
err := AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[1]})
So(err, ShouldBeNil)
err = AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[2]})
err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[2]})
So(err, ShouldBeNil)
err = SetDashboardAcl(&m.SetDashboardAclCommand{DashboardId: 1, OrgId: 1, Permission: m.PERMISSION_EDIT, UserGroupId: groupId})
err = SetDashboardAcl(&m.SetDashboardAclCommand{DashboardId: 1, OrgId: 1, Permission: m.PERMISSION_EDIT, TeamId: groupId})
err = DeleteUserGroup(&m.DeleteUserGroupCommand{Id: groupId})
err = DeleteTeam(&m.DeleteTeamCommand{Id: groupId})
So(err, ShouldBeNil)
query := &m.GetUserGroupByIdQuery{Id: groupId}
err = GetUserGroupById(query)
So(err, ShouldEqual, m.ErrUserGroupNotFound)
query := &m.GetTeamByIdQuery{Id: groupId}
err = GetTeamById(query)
So(err, ShouldEqual, m.ErrTeamNotFound)
permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: 1}
err = GetDashboardAclInfoList(permQuery)
......
......@@ -442,7 +442,7 @@ func DeleteUser(cmd *m.DeleteUserCommand) error {
"DELETE FROM org_user WHERE user_id = ?",
"DELETE FROM dashboard_acl WHERE user_id = ?",
"DELETE FROM preferences WHERE user_id = ?",
"DELETE FROM user_group_member WHERE user_id = ?",
"DELETE FROM team_member WHERE user_id = ?",
}
for _, sql := range deletes {
......
......@@ -10,9 +10,9 @@ const template = `
</gf-form-dropdown>
</div>
`;
export class UserGroupPickerCtrl {
export class TeamPickerCtrl {
group: any;
userGroupPicked: any;
teamPicked: any;
debouncedSearchGroups: any;
/** @ngInject */
......@@ -26,34 +26,34 @@ export class UserGroupPickerCtrl {
}
searchGroups(query: string) {
return Promise.resolve(this.backendSrv.get('/api/user-groups/search?perpage=10&page=1&query=' + query).then(result => {
return _.map(result.userGroups, ug => {
return Promise.resolve(this.backendSrv.get('/api/teams/search?perpage=10&page=1&query=' + query).then(result => {
return _.map(result.teams, ug => {
return {text: ug.name, value: ug};
});
}));
}
onChange(option) {
this.userGroupPicked({$group: option.value});
this.teamPicked({$group: option.value});
}
}
export function userGroupPicker() {
export function teamPicker() {
return {
restrict: 'E',
template: template,
controller: UserGroupPickerCtrl,
controller: TeamPickerCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: {
userGroupPicked: '&',
teamPicked: '&',
},
link: function(scope, elem, attrs, ctrl) {
scope.$on("user-group-picker-reset", () => {
scope.$on("team-picker-reset", () => {
ctrl.reset();
});
}
};
}
coreModule.directive('userGroupPicker', userGroupPicker);
coreModule.directive('teamPicker', teamPicker);
......@@ -47,7 +47,7 @@ import {helpModal} from './components/help/help';
import {JsonExplorer} from './components/json_explorer/json_explorer';
import {NavModelSrv, NavModel} from './nav_model_srv';
import {userPicker} from './components/user_picker';
import {userGroupPicker} from './components/user_group_picker';
import {teamPicker} from './components/team_picker';
import {geminiScrollbar} from './components/scroll/scroll';
import {gfPageDirective} from './components/gf_page';
import {orgSwitcher} from './components/org_switcher';
......@@ -85,7 +85,7 @@ export {
NavModelSrv,
NavModel,
userPicker,
userGroupPicker,
teamPicker,
geminiScrollbar,
gfPageDirective,
orgSwitcher,
......
......@@ -109,15 +109,15 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
controller : 'OrgApiKeysCtrl',
resolve: loadOrgBundle,
})
.when('/org/user-groups', {
templateUrl: 'public/app/features/org/partials/user_groups.html',
controller : 'UserGroupsCtrl',
.when('/org/teams', {
templateUrl: 'public/app/features/org/partials/teams.html',
controller : 'TeamsCtrl',
controllerAs: 'ctrl',
resolve: loadOrgBundle,
})
.when('/org/user-groups/edit/:id', {
templateUrl: 'public/app/features/org/partials/user_group_details.html',
controller : 'UserGroupDetailsCtrl',
.when('/org/teams/edit/:id', {
templateUrl: 'public/app/features/org/partials/team_details.html',
controller : 'TeamDetailsCtrl',
controllerAs: 'ctrl',
resolve: loadOrgBundle,
})
......
......@@ -52,7 +52,7 @@
<user-picker user-picked="ctrl.userPicked($user)"></user-picker>
</div>
<div class="gf-form" ng-show="ctrl.newType === 'Group'">
<user-group-picker user-group-picked="ctrl.groupPicked($group)"></user-group-picker>
<team-picker team-picked="ctrl.groupPicked($group)"></team-picker>
</div>
</div>
</form>
......@@ -101,9 +101,9 @@
<!-- </a> -->
<!-- </td> -->
<!-- </tr> -->
<!-- <tr ng&#45;repeat="permission in ctrl.userGroupPermissions" class="permissionlist__item"> -->
<!-- <tr ng&#45;repeat="permission in ctrl.teamPermissions" class="permissionlist__item"> -->
<!-- <td><i class="fa fa&#45;fw fa&#45;users"></i></td> -->
<!-- <td>{{permission.userGroup}}</td> -->
<!-- <td>{{permission.team}}</td> -->
<!-- <td><select class="gf&#45;form&#45;input gf&#45;size&#45;auto" ng&#45;model="permission.permissions" ng&#45;options="p.value as p.text for p in ctrl.permissionTypeOptions" ng&#45;change="ctrl.updatePermission(permission)"></select></td> -->
<!-- <td class="text&#45;right"> -->
<!-- <a ng&#45;click="ctrl.removePermission(permission)" class="btn btn&#45;danger btn&#45;small"> -->
......
......@@ -12,7 +12,7 @@ export class AclCtrl {
{value: 4, text: 'Admin'}
];
aclTypes = [
{value: 'Group', text: 'User Group'},
{value: 'Group', text: 'Team'},
{value: 'User', text: 'User'},
{value: 'Viewer', text: 'Everyone With Viewer Role'},
{value: 'Editor', text: 'Everyone With Editor Role'}
......@@ -58,10 +58,10 @@ export class AclCtrl {
item.nameHtml = this.$sce.trustAsHtml(item.userLogin);
item.sortName = item.userLogin;
item.sortRank = 10;
} else if (item.userGroupId > 0) {
} else if (item.teamId > 0) {
item.icon = "fa fa-fw fa-users";
item.nameHtml = this.$sce.trustAsHtml(item.userGroup);
item.sortName = item.userGroup;
item.nameHtml = this.$sce.trustAsHtml(item.team);
item.sortName = item.team;
item.sortRank = 20;
} else if (item.role) {
item.icon = "fa fa-fw fa-street-view";
......@@ -89,7 +89,7 @@ export class AclCtrl {
updated.push({
id: item.id,
userId: item.userId,
userGroupId: item.userGroupId,
teamId: item.teamId,
role: item.role,
permission: item.permission,
});
......@@ -144,7 +144,7 @@ export class AclCtrl {
return (origItem.role && newItem.role && origItem.role === newItem.role) ||
(origItem.userId && newItem.userId && origItem.userId === newItem.userId) ||
(origItem.userGroupId && newItem.userGroupId && origItem.userGroupId === newItem.userGroupId);
(origItem.teamId && newItem.teamId && origItem.teamId === newItem.teamId);
}
userPicked(user) {
......@@ -153,8 +153,8 @@ export class AclCtrl {
}
groupPicked(group) {
this.addNewItem({userGroupId: group.id, userGroup: group.name, permission: 1});
this.$scope.$broadcast('user-group-picker-reset');
this.addNewItem({teamId: group.id, team: group.name, permission: 1});
this.$scope.$broadcast('team-picker-reset');
}
removeItem(index) {
......@@ -179,7 +179,7 @@ export function dashAclModal() {
export interface FormModel {
dashboardId: number;
userId?: number;
userGroupId?: number;
teamId?: number;
PermissionType: number;
}
......@@ -189,8 +189,8 @@ export interface DashboardAcl {
userId?: number;
userLogin?: string;
userEmail?: string;
userGroupId?: number;
userGroup?: string;
teamId?: number;
team?: string;
permission?: number;
permissionName?: string;
role?: string;
......
......@@ -40,12 +40,12 @@ describe('AclCtrl', () => {
ctx.ctrl.userPicked(userItem);
const userGroupItem = {
const teamItem = {
id: 2,
name: 'ug1',
};
ctx.ctrl.groupPicked(userGroupItem);
ctx.ctrl.groupPicked(teamItem);
ctx.ctrl.newType = 'Editor';
ctx.ctrl.typeChanged();
......@@ -54,10 +54,10 @@ describe('AclCtrl', () => {
ctx.ctrl.typeChanged();
});
it('should sort the result by role, user group and user', () => {
it('should sort the result by role, team and user', () => {
expect(ctx.ctrl.items[0].role).to.eql('Viewer');
expect(ctx.ctrl.items[1].role).to.eql('Editor');
expect(ctx.ctrl.items[2].userGroupId).to.eql(2);
expect(ctx.ctrl.items[2].teamId).to.eql(2);
expect(ctx.ctrl.items[3].userId).to.eql(2);
});
......@@ -71,7 +71,7 @@ describe('AclCtrl', () => {
expect(backendSrv.post.getCall(0).args[1].items[0].permission).to.eql(1);
expect(backendSrv.post.getCall(0).args[1].items[1].role).to.eql('Editor');
expect(backendSrv.post.getCall(0).args[1].items[1].permission).to.eql(1);
expect(backendSrv.post.getCall(0).args[1].items[2].userGroupId).to.eql(2);
expect(backendSrv.post.getCall(0).args[1].items[2].teamId).to.eql(2);
expect(backendSrv.post.getCall(0).args[1].items[2].permission).to.eql(1);
expect(backendSrv.post.getCall(0).args[1].items[3].userId).to.eql(2);
expect(backendSrv.post.getCall(0).args[1].items[3].permission).to.eql(1);
......@@ -124,19 +124,19 @@ describe('AclCtrl', () => {
});
});
describe('when duplicate user group permissions are added', () => {
describe('when duplicate team permissions are added', () => {
beforeEach(() => {
backendSrv.get.reset();
backendSrv.post.reset();
ctx.ctrl.items = [];
const userGroupItem = {
const teamItem = {
id: 2,
name: 'ug1',
};
ctx.ctrl.groupPicked(userGroupItem);
ctx.ctrl.groupPicked(userGroupItem);
ctx.ctrl.groupPicked(teamItem);
ctx.ctrl.groupPicked(teamItem);
});
it('should throw a validation error', () => {
......@@ -148,25 +148,25 @@ describe('AclCtrl', () => {
});
});
describe('when one inherited and one not inherited user group permission are added', () => {
describe('when one inherited and one not inherited team permission are added', () => {
beforeEach(() => {
backendSrv.get.reset();
backendSrv.post.reset();
ctx.ctrl.items = [];
const inheritedUserGroupItem = {
const inheritedTeamItem = {
id: 2,
name: 'ug1',
dashboardId: -1
};
ctx.ctrl.items.push(inheritedUserGroupItem);
ctx.ctrl.items.push(inheritedTeamItem);
const userGroupItem = {
const teamItem = {
id: 2,
name: 'ug1',
};
ctx.ctrl.groupPicked(userGroupItem);
ctx.ctrl.groupPicked(teamItem);
});
it('should not throw a validation error', () => {
......
......@@ -5,7 +5,9 @@ import './select_org_ctrl';
import './change_password_ctrl';
import './new_org_ctrl';
import './user_invite_ctrl';
import './user_groups_ctrl';
import './teams_ctrl';
import './team_details_ctrl';
import './create_team_modal';
import './org_api_keys_ctrl';
import './org_details_ctrl';
import './prefs_control';
......@@ -3,17 +3,17 @@
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
export class CreateUserGroupCtrl {
userGroupName = '';
export class CreateTeamCtrl {
teamName = '';
/** @ngInject */
constructor(private backendSrv, private $location) {
}
createUserGroup() {
this.backendSrv.post('/api/user-groups', {name: this.userGroupName}).then((result) => {
if (result.userGroupId) {
this.$location.path('/org/user-groups/edit/' + result.userGroupId);
createTeam() {
this.backendSrv.post('/api/teams', {name: this.teamName}).then((result) => {
if (result.teamId) {
this.$location.path('/org/teams/edit/' + result.teamId);
}
this.dismiss();
});
......@@ -24,14 +24,14 @@ export class CreateUserGroupCtrl {
}
}
export function createUserGroupModal() {
export function createTeamModal() {
return {
restrict: 'E',
templateUrl: 'public/app/features/org/partials/create_user_group.html',
controller: CreateUserGroupCtrl,
templateUrl: 'public/app/features/org/partials/create_team.html',
controller: CreateTeamCtrl,
bindToController: true,
controllerAs: 'ctrl',
};
}
coreModule.directive('createUserGroupModal', createUserGroupModal);
coreModule.directive('createTeamModal', createTeamModal);
<div class="modal-body">
<div class="modal-header">
<h2 class="modal-header-title">
<span class="p-l-1">Create User Group</span>
<span class="p-l-1">Create Team</span>
</h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();">
......@@ -10,14 +10,14 @@
</div>
<div class="modal-content">
<form name="ctrl.createUserGroupForm" class="gf-form-group" novalidate>
<form name="ctrl.createTeamForm" class="gf-form-group" novalidate>
<div class="p-t-2">
<div class="gf-form-inline">
<div class="gf-form max-width-21">
<input type="text" class="gf-form-input" ng-model='ctrl.userGroupName' required give-focus="true" placeholder="Enter User Group Name"></input>
<input type="text" class="gf-form-input" ng-model='ctrl.teamName' required give-focus="true" placeholder="Enter Team Name"></input>
</div>
<div class="gf-form">
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createUserGroup();ctrl.dismiss();">Create</button>
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createTeam();ctrl.dismiss();">Create</button>
</div>
</div>
</div>
......
......@@ -2,13 +2,13 @@
<div class="page-container">
<div class="page-header">
<h1>Edit User Group</h1>
<h1>Edit Team</h1>
</div>
<form name="userGroupDetailsForm" class="gf-form-group gf-form-inline">
<form name="teamDetailsForm" class="gf-form-group gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-10">Name</span>
<input type="text" required ng-model="ctrl.userGroup.name" class="gf-form-input max-width-14" >
<input type="text" required ng-model="ctrl.team.name" class="gf-form-input max-width-14" >
</div>
<div class="gf-form">
......@@ -17,7 +17,7 @@
</form>
<div class="gf-form-group">
<h3 class="page-heading">User Group Members</h3>
<h3 class="page-heading">Team Members</h3>
<form name="ctrl.addMemberForm" class="gf-form-group">
<div class="gf-form">
......@@ -26,24 +26,24 @@
</div>
</form>
<table class="grafana-options-table" ng-show="ctrl.userGroupMembers.length > 0">
<table class="grafana-options-table" ng-show="ctrl.teamMembers.length > 0">
<tr>
<th>Username</th>
<th>Email</th>
<th></th>
</tr>
<tr ng-repeat="member in ctrl.userGroupMembers">
<tr ng-repeat="member in ctrl.teamMembers">
<td>{{member.login}}</td>
<td>{{member.email}}</td>
<td style="width: 1%">
<a ng-click="ctrl.removeUserGroupMember(member)" class="btn btn-danger btn-mini">
<a ng-click="ctrl.removeTeamMember(member)" class="btn btn-danger btn-mini">
<i class="fa fa-remove"></i>
</a>
</td>
</tr>
</table>
<div>
<em class="muted" ng-hide="ctrl.userGroupMembers.length > 0">
This user group has no members yet.
<em class="muted" ng-hide="ctrl.teamMembers.length > 0">
This team has no members yet.
</em>
</div>
......@@ -5,20 +5,20 @@
<div class="gf-form gf-form--grow">
<label class="gf-form-label">Search</label>
<input type="text" class="gf-form-input max-width-20" placeholder="Find User Group by name" tabindex="1" give-focus="true"
<input type="text" class="gf-form-input max-width-20" placeholder="Find Team by name" tabindex="1" give-focus="true"
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.get()" />
</div>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-success" ng-click="ctrl.openUserGroupModal()">
<a class="btn btn-success" ng-click="ctrl.openTeamModal()">
<i class="fa fa-plus"></i>
Add Team
</a>
</div>
<div class="admin-list-table">
<table class="filter-table form-inline" ng-show="ctrl.userGroups.length > 0">
<table class="filter-table form-inline" ng-show="ctrl.teams.length > 0">
<thead>
<tr>
<th>Name</th>
......@@ -27,18 +27,18 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="userGroup in ctrl.userGroups">
<tr ng-repeat="team in ctrl.teams">
<td>
<a href="org/user-groups/edit/{{userGroup.id}}">{{userGroup.name}}</a>
<a href="org/teams/edit/{{team.id}}">{{team.name}}</a>
</td>
<td>#Count</td>
<td class="text-right">
<a href="org/user-groups/edit/{{userGroup.id}}" class="btn btn-inverse btn-small">
<a href="org/teams/edit/{{team.id}}" class="btn btn-inverse btn-small">
<i class="fa fa-edit"></i>
Edit
</a>
&nbsp;&nbsp;
<a ng-click="ctrl.deleteUserGroup(userGroup)" class="btn btn-danger btn-small">
<a ng-click="ctrl.deleteTeam(team)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i>
</a>
</td>
......@@ -58,7 +58,7 @@
</ol>
</div>
<em class="muted" ng-hide="ctrl.userGroups.length > 0">
No User Groups found.
<em class="muted" ng-hide="ctrl.teams.length > 0">
No Teams found.
</em>
</div>
import '../user_group_details_ctrl';
import '../team_details_ctrl';
import {describe, beforeEach, it, expect, sinon, angularMocks} from 'test/lib/common';
import UserGroupDetailsCtrl from '../user_group_details_ctrl';
import TeamDetailsCtrl from '../team_details_ctrl';
describe('UserGroupDetailsCtrl', () => {
describe('TeamDetailsCtrl', () => {
var ctx: any = {};
var backendSrv = {
searchUsers: sinon.stub().returns(Promise.resolve([])),
......@@ -16,7 +16,7 @@ var backendSrv = {
beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.ctrl = $controller(UserGroupDetailsCtrl, {
ctx.ctrl = $controller(TeamDetailsCtrl, {
$scope: ctx.scope,
backendSrv: backendSrv,
$routeParams: {id: 1},
......@@ -24,7 +24,7 @@ var backendSrv = {
});
}));
describe('when user is chosen to be added to user group', () => {
describe('when user is chosen to be added to team', () => {
beforeEach(() => {
const userItem = {
id: 2,
......@@ -34,13 +34,13 @@ var backendSrv = {
});
it('should parse the result and save to db', () => {
expect(backendSrv.post.getCall(0).args[0]).to.eql('/api/user-groups/1/members');
expect(backendSrv.post.getCall(0).args[0]).to.eql('/api/teams/1/members');
expect(backendSrv.post.getCall(0).args[1].userId).to.eql(2);
});
it('should refresh the list after saving.', () => {
expect(backendSrv.get.getCall(0).args[0]).to.eql('/api/user-groups/1');
expect(backendSrv.get.getCall(1).args[0]).to.eql('/api/user-groups/1/members');
expect(backendSrv.get.getCall(0).args[0]).to.eql('/api/teams/1');
expect(backendSrv.get.getCall(1).args[0]).to.eql('/api/teams/1/members');
});
});
});
......@@ -2,9 +2,9 @@
import coreModule from 'app/core/core_module';
export default class UserGroupDetailsCtrl {
userGroup: UserGroup;
userGroupMembers: User[] = [];
export default class TeamDetailsCtrl {
team: Team;
teamMembers: User[] = [];
navModel: any;
constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) {
......@@ -14,49 +14,49 @@ export default class UserGroupDetailsCtrl {
get() {
if (this.$routeParams && this.$routeParams.id) {
this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}`)
this.backendSrv.get(`/api/teams/${this.$routeParams.id}`)
.then(result => {
this.userGroup = result;
this.team = result;
});
this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}/members`)
this.backendSrv.get(`/api/teams/${this.$routeParams.id}/members`)
.then(result => {
this.userGroupMembers = result;
this.teamMembers = result;
});
}
}
removeUserGroupMember(userGroupMember: UserGroupMember) {
removeTeamMember(teamMember: TeamMember) {
this.$scope.appEvent('confirm-modal', {
title: 'Remove Member',
text: 'Are you sure you want to remove ' + userGroupMember.name + ' from this group?',
text: 'Are you sure you want to remove ' + teamMember.name + ' from this group?',
yesText: "Remove",
icon: "fa-warning",
onConfirm: () => {
this.removeMemberConfirmed(userGroupMember);
this.removeMemberConfirmed(teamMember);
}
});
}
removeMemberConfirmed(userGroupMember: UserGroupMember) {
this.backendSrv.delete(`/api/user-groups/${this.$routeParams.id}/members/${userGroupMember.userId}`)
removeMemberConfirmed(teamMember: TeamMember) {
this.backendSrv.delete(`/api/teams/${this.$routeParams.id}/members/${teamMember.userId}`)
.then(this.get.bind(this));
}
update() {
if (!this.$scope.userGroupDetailsForm.$valid) { return; }
if (!this.$scope.teamDetailsForm.$valid) { return; }
this.backendSrv.put('/api/user-groups/' + this.userGroup.id, {name: this.userGroup.name});
this.backendSrv.put('/api/teams/' + this.team.id, {name: this.team.name});
}
userPicked(user) {
this.backendSrv.post(`/api/user-groups/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
this.backendSrv.post(`/api/teams/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
this.$scope.$broadcast('user-picker-reset');
this.get();
});
}
}
export interface UserGroup {
export interface Team {
id: number;
name: string;
}
......@@ -68,10 +68,10 @@ export interface User {
email: string;
}
export interface UserGroupMember {
export interface TeamMember {
userId: number;
name: string;
}
coreModule.controller('UserGroupDetailsCtrl', UserGroupDetailsCtrl);
coreModule.controller('TeamDetailsCtrl', TeamDetailsCtrl);
......@@ -3,8 +3,8 @@
import coreModule from 'app/core/core_module';
import {appEvents} from 'app/core/core';
export class UserGroupsCtrl {
userGroups: any;
export class TeamsCtrl {
teams: any;
pages = [];
perPage = 50;
page = 1;
......@@ -20,9 +20,9 @@ export class UserGroupsCtrl {
}
get() {
this.backendSrv.get(`/api/user-groups/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`)
this.backendSrv.get(`/api/teams/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`)
.then((result) => {
this.userGroups = result.userGroups;
this.teams = result.teams;
this.page = result.page;
this.perPage = result.perPage;
this.totalPages = Math.ceil(result.totalCount / result.perPage);
......@@ -40,29 +40,29 @@ export class UserGroupsCtrl {
this.get();
}
deleteUserGroup(userGroup) {
deleteTeam(team) {
appEvents.emit('confirm-modal', {
title: 'Delete',
text: 'Are you sure you want to delete User Group ' + userGroup.name + '?',
text: 'Are you sure you want to delete Team ' + team.name + '?',
yesText: "Delete",
icon: "fa-warning",
onConfirm: () => {
this.deleteUserGroupConfirmed(userGroup);
this.deleteTeamConfirmed(team);
}
});
}
deleteUserGroupConfirmed(userGroup) {
this.backendSrv.delete('/api/user-groups/' + userGroup.id)
deleteTeamConfirmed(team) {
this.backendSrv.delete('/api/teams/' + team.id)
.then(this.get.bind(this));
}
openUserGroupModal() {
openTeamModal() {
appEvents.emit('show-modal', {
templateHtml: '<create-user-group-modal></create-user-group-modal>',
templateHtml: '<create-team-modal></create-team-modal>',
modalClass: 'modal--narrow'
});
}
}
coreModule.controller('UserGroupsCtrl', UserGroupsCtrl);
coreModule.controller('TeamsCtrl', TeamsCtrl);
......@@ -13,14 +13,14 @@
</div>
</div>
</li>
<li class="card-item-wrapper" ng-repeat="permission in ctrl.userGroupPermissions">
<li class="card-item-wrapper" ng-repeat="permission in ctrl.teamPermissions">
<div class="card-item card-item--alert">
<div class="card-item-header">
<div class="card-item-sub-name">{{permission.permissionName}}</div>
</div>
<div class="card-item-body">
<div class="card-item-details">
<div class="card-item-notice">{{permission.userGroup}}</div>
<div class="card-item-notice">{{permission.team}}</div>
</div>
</div>
</div>
......
......@@ -7,7 +7,7 @@ class PermissionListCtrl extends PanelCtrl {
static templateUrl = 'module.html';
userPermissions: any[];
userGroupPermissions: any[];
teamPermissions: any[];
roles: any[];
panelDefaults = {
......@@ -48,7 +48,7 @@ class PermissionListCtrl extends PanelCtrl {
return this.backendSrv.get(`/api/dashboards/id/${this.panel.folderId}/acl`)
.then(result => {
this.userPermissions = _.filter(result, p => { return p.userId > 0;});
this.userGroupPermissions = _.filter(result, p => { return p.userGroupId > 0;});
this.teamPermissions = _.filter(result, p => { return p.teamId > 0;});
// this.roles = this.setRoles(result);
});
}
......
......@@ -51,8 +51,8 @@
background-image: url('../img/icons_#{$theme-name}_theme/icon_notification_channels.svg');
}
.gicon-user-group {
background-image: url('../img/icons_#{$theme-name}_theme/icon_user_group.svg');
.gicon-team {
background-image: url('../img/icons_#{$theme-name}_theme/icon_team.svg');
}
.gicon-org {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment