Ver código fonte

Merge branch 'refactor-user-group-to-team' of https://github.com/alexanderzobnin/grafana into user-group-to-team

Torkel Ödegaard 8 anos atrás
pai
commit
05cc1f85c3
52 arquivos alterados com 922 adições e 920 exclusões
  1. 1 1
      CHANGELOG.md
  2. 1 1
      ROADMAP.md
  3. 10 10
      pkg/api/api.go
  4. 1 1
      pkg/api/dashboard_acl.go
  5. 8 8
      pkg/api/dashboard_acl_test.go
  6. 4 4
      pkg/api/dashboard_test.go
  7. 4 4
      pkg/api/dtos/acl.go
  8. 2 2
      pkg/api/index.go
  9. 92 0
      pkg/api/team.go
  10. 44 0
      pkg/api/team_members.go
  11. 13 13
      pkg/api/team_test.go
  12. 0 92
      pkg/api/user_group.go
  13. 0 44
      pkg/api/user_group_members.go
  14. 8 8
      pkg/models/dashboard_acl.go
  15. 68 0
      pkg/models/team.go
  16. 55 0
      pkg/models/team_member.go
  17. 0 68
      pkg/models/user_group.go
  18. 0 55
      pkg/models/user_group_member.go
  19. 10 10
      pkg/services/guardian/guardian.go
  20. 13 13
      pkg/services/sqlstore/dashboard_acl.go
  21. 11 11
      pkg/services/sqlstore/dashboard_acl_test.go
  22. 2 2
      pkg/services/sqlstore/migrations/dashboard_acl.go
  23. 1 1
      pkg/services/sqlstore/migrations/migrations.go
  24. 13 13
      pkg/services/sqlstore/migrations/team_mig.go
  25. 1 1
      pkg/services/sqlstore/org_users.go
  26. 1 1
      pkg/services/sqlstore/search_builder.go
  27. 233 0
      pkg/services/sqlstore/team.go
  28. 114 0
      pkg/services/sqlstore/team_test.go
  29. 1 1
      pkg/services/sqlstore/user.go
  30. 0 233
      pkg/services/sqlstore/user_group.go
  31. 0 114
      pkg/services/sqlstore/user_group_test.go
  32. 10 10
      public/app/core/components/team_picker.ts
  33. 2 2
      public/app/core/core.ts
  34. 6 6
      public/app/core/routes/routes.ts
  35. 3 3
      public/app/features/dashboard/acl/acl.html
  36. 11 11
      public/app/features/dashboard/acl/acl.ts
  37. 14 14
      public/app/features/dashboard/acl/specs/acl_specs.ts
  38. 3 1
      public/app/features/org/all.ts
  39. 37 0
      public/app/features/org/create_team_modal.ts
  40. 0 37
      public/app/features/org/create_user_group_modal.ts
  41. 4 4
      public/app/features/org/partials/create_team.html
  42. 9 9
      public/app/features/org/partials/team_details.html
  43. 9 9
      public/app/features/org/partials/teams.html
  44. 8 8
      public/app/features/org/specs/team_details_ctrl_specs.ts
  45. 77 0
      public/app/features/org/team_details_ctrl.ts
  46. 12 12
      public/app/features/org/teams_ctrl.ts
  47. 0 77
      public/app/features/org/user_group_details_ctrl.ts
  48. 2 2
      public/app/plugins/panel/permissionlist/module.html
  49. 2 2
      public/app/plugins/panel/permissionlist/module.ts
  50. 0 0
      public/img/icons_dark_theme/icon_team.svg
  51. 0 0
      public/img/icons_light_theme/icon_team.svg
  52. 2 2
      public/sass/base/_icons.scss

+ 1 - 1
CHANGELOG.md

@@ -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

+ 1 - 1
ROADMAP.md

@@ -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

+ 10 - 10
pkg/api/api.go

@@ -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.

+ 1 - 1
pkg/api/dashboard_acl.go

@@ -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(),

+ 8 - 8
pkg/api/dashboard_acl_test.go

@@ -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)
 	}

+ 4 - 4
pkg/api/dashboard_test.go

@@ -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
 		})
 

+ 4 - 4
pkg/api/dtos/acl.go

@@ -9,8 +9,8 @@ type UpdateDashboardAclCommand struct {
 }
 
 type DashboardAclUpdateItem struct {
-	UserId      int64            `json:"userId"`
-	UserGroupId int64            `json:"userGroupId"`
-	Role        *m.RoleType      `json:"role,omitempty"`
-	Permission  m.PermissionType `json:"permission"`
+	UserId     int64            `json:"userId"`
+	TeamId     int64            `json:"teamId"`
+	Role       *m.RoleType      `json:"role,omitempty"`
+	Permission m.PermissionType `json:"permission"`
 }

+ 2 - 2
pkg/api/index.go

@@ -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",

+ 92 - 0
pkg/api/team.go

@@ -0,0 +1,92 @@
+package api
+
+import (
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/util"
+)
+
+// 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.ErrTeamNameTaken {
+			return ApiError(409, "Team name taken", err)
+		}
+		return ApiError(500, "Failed to create Team", err)
+	}
+
+	return Json(200, &util.DynMap{
+		"teamId":  cmd.Result.Id,
+		"message": "Team created",
+	})
+}
+
+// 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.ErrTeamNameTaken {
+			return ApiError(400, "Team name taken", err)
+		}
+		return ApiError(500, "Failed to update Team", err)
+	}
+
+	return ApiSuccess("Team updated")
+}
+
+// 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 Team", err)
+	}
+	return ApiSuccess("Team deleted")
+}
+
+// GET /api/teams/search
+func SearchTeams(c *middleware.Context) Response {
+	perPage := c.QueryInt("perpage")
+	if perPage <= 0 {
+		perPage = 1000
+	}
+	page := c.QueryInt("page")
+	if page < 1 {
+		page = 1
+	}
+
+	query := m.SearchTeamsQuery{
+		Query: c.Query("query"),
+		Name:  c.Query("name"),
+		Page:  page,
+		Limit: perPage,
+		OrgId: c.OrgId,
+	}
+
+	if err := bus.Dispatch(&query); err != nil {
+		return ApiError(500, "Failed to search Teams", err)
+	}
+
+	query.Result.Page = page
+	query.Result.PerPage = perPage
+
+	return Json(200, query.Result)
+}
+
+// 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.ErrTeamNotFound {
+			return ApiError(404, "Team not found", err)
+		}
+
+		return ApiError(500, "Failed to get Team", err)
+	}
+
+	return Json(200, &query.Result)
+}

+ 44 - 0
pkg/api/team_members.go

@@ -0,0 +1,44 @@
+package api
+
+import (
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/util"
+)
+
+// 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 Team Members", err)
+	}
+
+	return Json(200, query.Result)
+}
+
+// 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.ErrTeamMemberAlreadyAdded {
+			return ApiError(400, "User is already added to this team", err)
+		}
+		return ApiError(500, "Failed to add Member to Team", err)
+	}
+
+	return Json(200, &util.DynMap{
+		"message": "Member added to Team",
+	})
+}
+
+// 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("Team Member removed")
+}

+ 13 - 13
pkg/api/user_group_test.go → pkg/api/team_test.go

@@ -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)

+ 0 - 92
pkg/api/user_group.go

@@ -1,92 +0,0 @@
-package api
-
-import (
-	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
-	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/util"
-)
-
-// POST /api/user-groups
-func CreateUserGroup(c *middleware.Context, cmd m.CreateUserGroupCommand) Response {
-	cmd.OrgId = c.OrgId
-	if err := bus.Dispatch(&cmd); err != nil {
-		if err == m.ErrUserGroupNameTaken {
-			return ApiError(409, "User Group name taken", err)
-		}
-		return ApiError(500, "Failed to create User Group", err)
-	}
-
-	return Json(200, &util.DynMap{
-		"userGroupId": cmd.Result.Id,
-		"message":     "User Group created",
-	})
-}
-
-// PUT /api/user-groups/:userGroupId
-func UpdateUserGroup(c *middleware.Context, cmd m.UpdateUserGroupCommand) Response {
-	cmd.Id = c.ParamsInt64(":userGroupId")
-	if err := bus.Dispatch(&cmd); err != nil {
-		if err == m.ErrUserGroupNameTaken {
-			return ApiError(400, "User Group name taken", err)
-		}
-		return ApiError(500, "Failed to update User Group", err)
-	}
-
-	return ApiSuccess("User Group 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)
-		}
-		return ApiError(500, "Failed to update User Group", err)
-	}
-	return ApiSuccess("User Group deleted")
-}
-
-// GET /api/user-groups/search
-func SearchUserGroups(c *middleware.Context) Response {
-	perPage := c.QueryInt("perpage")
-	if perPage <= 0 {
-		perPage = 1000
-	}
-	page := c.QueryInt("page")
-	if page < 1 {
-		page = 1
-	}
-
-	query := m.SearchUserGroupsQuery{
-		Query: c.Query("query"),
-		Name:  c.Query("name"),
-		Page:  page,
-		Limit: perPage,
-		OrgId: c.OrgId,
-	}
-
-	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(500, "Failed to search User Groups", err)
-	}
-
-	query.Result.Page = page
-	query.Result.PerPage = perPage
-
-	return Json(200, query.Result)
-}
-
-// GET /api/user-groups/:userGroupId
-func GetUserGroupById(c *middleware.Context) Response {
-	query := m.GetUserGroupByIdQuery{Id: c.ParamsInt64(":userGroupId")}
-
-	if err := bus.Dispatch(&query); err != nil {
-		if err == m.ErrUserGroupNotFound {
-			return ApiError(404, "User Group not found", err)
-		}
-
-		return ApiError(500, "Failed to get User Group", err)
-	}
-
-	return Json(200, &query.Result)
-}

+ 0 - 44
pkg/api/user_group_members.go

@@ -1,44 +0,0 @@
-package api
-
-import (
-	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/middleware"
-	m "github.com/grafana/grafana/pkg/models"
-	"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")}
-
-	if err := bus.Dispatch(&query); err != nil {
-		return ApiError(500, "Failed to get User Group 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")
-	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)
-		}
-		return ApiError(500, "Failed to add Member to User Group", err)
-	}
-
-	return Json(200, &util.DynMap{
-		"message": "Member added to User Group",
-	})
-}
-
-// 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)
-	}
-	return ApiSuccess("User Group Member removed")
-}

+ 8 - 8
pkg/models/dashboard_acl.go

@@ -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.")
 )
 
@@ -34,10 +34,10 @@ type DashboardAcl struct {
 	OrgId       int64
 	DashboardId int64
 
-	UserId      int64
-	UserGroupId int64
-	Role        *RoleType // pointer to be nullable
-	Permission  PermissionType
+	UserId     int64
+	TeamId     int64
+	Role       *RoleType // pointer to be nullable
+	Permission PermissionType
 
 	Created time.Time
 	Updated time.Time
@@ -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

+ 68 - 0
pkg/models/team.go

@@ -0,0 +1,68 @@
+package models
+
+import (
+	"errors"
+	"time"
+)
+
+// Typed errors
+var (
+	ErrTeamNotFound  = errors.New("Team not found")
+	ErrTeamNameTaken = errors.New("Team name is taken")
+)
+
+// Team model
+type Team struct {
+	Id    int64  `json:"id"`
+	OrgId int64  `json:"orgId"`
+	Name  string `json:"name"`
+
+	Created time.Time `json:"created"`
+	Updated time.Time `json:"updated"`
+}
+
+// ---------------------
+// COMMANDS
+
+type CreateTeamCommand struct {
+	Name  string `json:"name" binding:"Required"`
+	OrgId int64  `json:"-"`
+
+	Result Team `json:"-"`
+}
+
+type UpdateTeamCommand struct {
+	Id   int64
+	Name string
+}
+
+type DeleteTeamCommand struct {
+	Id int64
+}
+
+type GetTeamByIdQuery struct {
+	Id     int64
+	Result *Team
+}
+
+type GetTeamsByUserQuery struct {
+	UserId int64   `json:"userId"`
+	Result []*Team `json:"teams"`
+}
+
+type SearchTeamsQuery struct {
+	Query string
+	Name  string
+	Limit int
+	Page  int
+	OrgId int64
+
+	Result SearchTeamQueryResult
+}
+
+type SearchTeamQueryResult struct {
+	TotalCount int64   `json:"totalCount"`
+	Teams      []*Team `json:"teams"`
+	Page       int     `json:"page"`
+	PerPage    int     `json:"perPage"`
+}

+ 55 - 0
pkg/models/team_member.go

@@ -0,0 +1,55 @@
+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"`
+}

+ 0 - 68
pkg/models/user_group.go

@@ -1,68 +0,0 @@
-package models
-
-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 {
-	Id    int64  `json:"id"`
-	OrgId int64  `json:"orgId"`
-	Name  string `json:"name"`
-
-	Created time.Time `json:"created"`
-	Updated time.Time `json:"updated"`
-}
-
-// ---------------------
-// COMMANDS
-
-type CreateUserGroupCommand struct {
-	Name  string `json:"name" binding:"Required"`
-	OrgId int64  `json:"-"`
-
-	Result UserGroup `json:"-"`
-}
-
-type UpdateUserGroupCommand struct {
-	Id   int64
-	Name string
-}
-
-type DeleteUserGroupCommand struct {
-	Id int64
-}
-
-type GetUserGroupByIdQuery struct {
-	Id     int64
-	Result *UserGroup
-}
-
-type GetUserGroupsByUserQuery struct {
-	UserId int64        `json:"userId"`
-	Result []*UserGroup `json:"userGroups"`
-}
-
-type SearchUserGroupsQuery struct {
-	Query string
-	Name  string
-	Limit int
-	Page  int
-	OrgId int64
-
-	Result SearchUserGroupQueryResult
-}
-
-type SearchUserGroupQueryResult struct {
-	TotalCount int64        `json:"totalCount"`
-	UserGroups []*UserGroup `json:"userGroups"`
-	Page       int          `json:"page"`
-	PerPage    int          `json:"perPage"`
-}

+ 0 - 55
pkg/models/user_group_member.go

@@ -1,55 +0,0 @@
-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"`
-}

+ 10 - 10
pkg/services/guardian/guardian.go

@@ -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

+ 13 - 13
pkg/services/sqlstore/dashboard_acl.go

@@ -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

+ 11 - 11
pkg/services/sqlstore/dashboard_acl_test.go

@@ -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)
 				})
 
 			})

+ 2 - 2
pkg/services/sqlstore/migrations/dashboard_acl.go

@@ -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},
 		},
 	}
 

+ 1 - 1
pkg/services/sqlstore/migrations/migrations.go

@@ -26,7 +26,7 @@ func AddMigrations(mg *Migrator) {
 	addAnnotationMig(mg)
 	addTestDataMigrations(mg)
 	addDashboardVersionMigration(mg)
-	addUserGroupMigrations(mg)
+	addTeamMigrations(mg)
 	addDashboardAclMigrations(mg)
 	addTagMigration(mg)
 }

+ 13 - 13
pkg/services/sqlstore/migrations/user_group_mig.go → pkg/services/sqlstore/migrations/team_mig.go

@@ -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]))
 }

+ 1 - 1
pkg/services/sqlstore/org_users.go

@@ -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 {

+ 1 - 1
pkg/services/sqlstore/search_builder.go

@@ -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

+ 233 - 0
pkg/services/sqlstore/team.go

@@ -0,0 +1,233 @@
+package sqlstore
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+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", RemoveTeamMember)
+	bus.AddHandler("sql", GetTeamMembers)
+}
+
+func CreateTeam(cmd *m.CreateTeamCommand) error {
+	return inTransaction(func(sess *DBSession) error {
+
+		if isNameTaken, err := isTeamNameTaken(cmd.Name, 0, sess); err != nil {
+			return err
+		} else if isNameTaken {
+			return m.ErrTeamNameTaken
+		}
+
+		team := m.Team{
+			Name:    cmd.Name,
+			OrgId:   cmd.OrgId,
+			Created: time.Now(),
+			Updated: time.Now(),
+		}
+
+		_, err := sess.Insert(&team)
+
+		cmd.Result = team
+
+		return err
+	})
+}
+
+func UpdateTeam(cmd *m.UpdateTeamCommand) error {
+	return inTransaction(func(sess *DBSession) error {
+
+		if isNameTaken, err := isTeamNameTaken(cmd.Name, cmd.Id, sess); err != nil {
+			return err
+		} else if isNameTaken {
+			return m.ErrTeamNameTaken
+		}
+
+		team := m.Team{
+			Name:    cmd.Name,
+			Updated: time.Now(),
+		}
+
+		affectedRows, err := sess.Id(cmd.Id).Update(&team)
+
+		if err != nil {
+			return err
+		}
+
+		if affectedRows == 0 {
+			return m.ErrTeamNotFound
+		}
+
+		return nil
+	})
+}
+
+func DeleteTeam(cmd *m.DeleteTeamCommand) error {
+	return inTransaction(func(sess *DBSession) error {
+		if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.Id); err != nil {
+			return err
+		} else if len(res) != 1 {
+			return m.ErrTeamNotFound
+		}
+
+		deletes := []string{
+			"DELETE FROM team_member WHERE team_id = ?",
+			"DELETE FROM team WHERE id = ?",
+			"DELETE FROM dashboard_acl WHERE team_id = ?",
+		}
+
+		for _, sql := range deletes {
+			_, err := sess.Exec(sql, cmd.Id)
+			if err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+}
+
+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 != team.Id {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+func SearchTeams(query *m.SearchTeamsQuery) error {
+	query.Result = m.SearchTeamQueryResult{
+		Teams: make([]*m.Team, 0),
+	}
+	queryWithWildcards := "%" + query.Query + "%"
+
+	sess := x.Table("team")
+	sess.Where("org_id=?", query.OrgId)
+
+	if query.Query != "" {
+		sess.Where("name LIKE ?", queryWithWildcards)
+	}
+	if query.Name != "" {
+		sess.Where("name=?", query.Name)
+	}
+	sess.Asc("name")
+
+	offset := query.Limit * (query.Page - 1)
+	sess.Limit(query.Limit, offset)
+	sess.Cols("id", "name")
+	if err := sess.Find(&query.Result.Teams); err != nil {
+		return err
+	}
+
+	team := m.Team{}
+
+	countSess := x.Table("team")
+	if query.Query != "" {
+		countSess.Where("name LIKE ?", queryWithWildcards)
+	}
+	if query.Name != "" {
+		countSess.Where("name=?", query.Name)
+	}
+	count, err := countSess.Count(&team)
+	query.Result.TotalCount = count
+
+	return err
+}
+
+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.ErrTeamNotFound
+	}
+
+	query.Result = &team
+	return nil
+}
+
+func GetTeamsByUser(query *m.GetTeamsByUserQuery) error {
+	query.Result = make([]*m.Team, 0)
+
+	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 {
+		return err
+	}
+
+	return nil
+}
+
+func AddTeamMember(cmd *m.AddTeamMemberCommand) error {
+	return inTransaction(func(sess *DBSession) error {
+		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.ErrTeamMemberAlreadyAdded
+		}
+
+		if res, err := sess.Query("SELECT 1 from team WHERE id=?", cmd.TeamId); err != nil {
+			return err
+		} else if len(res) != 1 {
+			return m.ErrTeamNotFound
+		}
+
+		entity := m.TeamMember{
+			OrgId:   cmd.OrgId,
+			TeamId:  cmd.TeamId,
+			UserId:  cmd.UserId,
+			Created: time.Now(),
+			Updated: time.Now(),
+		}
+
+		_, err := sess.Insert(&entity)
+		return err
+	})
+}
+
+func RemoveTeamMember(cmd *m.RemoveTeamMemberCommand) error {
+	return inTransaction(func(sess *DBSession) error {
+		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
+		}
+
+		return err
+	})
+}
+
+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)
+	return err
+}

+ 114 - 0
pkg/services/sqlstore/team_test.go

@@ -0,0 +1,114 @@
+package sqlstore
+
+import (
+	"fmt"
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func TestTeamCommandsAndQueries(t *testing.T) {
+
+	Convey("Testing Team commands & queries", t, func() {
+		InitTestDB(t)
+
+		Convey("Given saved users and two teams", 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.CreateTeamCommand{Name: "group1 name"}
+			group2 := m.CreateTeamCommand{Name: "group2 name"}
+
+			err := CreateTeam(&group1)
+			So(err, ShouldBeNil)
+			err = CreateTeam(&group2)
+			So(err, ShouldBeNil)
+
+			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)
+
+				team1 := query.Result.Teams[0]
+				So(team1.Name, ShouldEqual, "group1 name")
+
+				err = AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: team1.Id, UserId: userIds[0]})
+				So(err, ShouldBeNil)
+
+				q1 := &m.GetTeamMembersQuery{TeamId: team1.Id}
+				err = GetTeamMembers(q1)
+				So(err, ShouldBeNil)
+				So(q1.Result[0].TeamId, ShouldEqual, team1.Id)
+				So(q1.Result[0].Login, ShouldEqual, "loginuser0")
+			})
+
+			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.Teams), ShouldEqual, 2)
+				So(query.Result.TotalCount, ShouldEqual, 2)
+
+				query2 := &m.SearchTeamsQuery{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(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[0]})
+
+				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 = RemoveTeamMember(&m.RemoveTeamMemberCommand{TeamId: group1.Result.Id, UserId: userIds[0]})
+				So(err, ShouldBeNil)
+
+				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 := AddTeamMember(&m.AddTeamMemberCommand{OrgId: 1, TeamId: groupId, UserId: userIds[1]})
+				So(err, ShouldBeNil)
+				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, TeamId: groupId})
+
+				err = DeleteTeam(&m.DeleteTeamCommand{Id: groupId})
+				So(err, ShouldBeNil)
+
+				query := &m.GetTeamByIdQuery{Id: groupId}
+				err = GetTeamById(query)
+				So(err, ShouldEqual, m.ErrTeamNotFound)
+
+				permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: 1}
+				err = GetDashboardAclInfoList(permQuery)
+				So(err, ShouldBeNil)
+
+				So(len(permQuery.Result), ShouldEqual, 0)
+			})
+		})
+	})
+}

+ 1 - 1
pkg/services/sqlstore/user.go

@@ -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 {

+ 0 - 233
pkg/services/sqlstore/user_group.go

@@ -1,233 +0,0 @@
-package sqlstore
-
-import (
-	"fmt"
-	"time"
-
-	"github.com/grafana/grafana/pkg/bus"
-	m "github.com/grafana/grafana/pkg/models"
-)
-
-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)
-}
-
-func CreateUserGroup(cmd *m.CreateUserGroupCommand) error {
-	return inTransaction(func(sess *DBSession) 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,
-			OrgId:   cmd.OrgId,
-			Created: time.Now(),
-			Updated: time.Now(),
-		}
-
-		_, err := sess.Insert(&userGroup)
-
-		cmd.Result = userGroup
-
-		return err
-	})
-}
-
-func UpdateUserGroup(cmd *m.UpdateUserGroupCommand) error {
-	return inTransaction(func(sess *DBSession) error {
-
-		if isNameTaken, err := isUserGroupNameTaken(cmd.Name, cmd.Id, sess); err != nil {
-			return err
-		} else if isNameTaken {
-			return m.ErrUserGroupNameTaken
-		}
-
-		userGroup := m.UserGroup{
-			Name:    cmd.Name,
-			Updated: time.Now(),
-		}
-
-		affectedRows, err := sess.Id(cmd.Id).Update(&userGroup)
-
-		if err != nil {
-			return err
-		}
-
-		if affectedRows == 0 {
-			return m.ErrUserGroupNotFound
-		}
-
-		return nil
-	})
-}
-
-func DeleteUserGroup(cmd *m.DeleteUserGroupCommand) error {
-	return inTransaction(func(sess *DBSession) 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 = ?",
-			"DELETE FROM dashboard_acl WHERE user_group_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 *DBSession) (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 = m.SearchUserGroupQueryResult{
-		UserGroups: make([]*m.UserGroup, 0),
-	}
-	queryWithWildcards := "%" + query.Query + "%"
-
-	sess := x.Table("user_group")
-	sess.Where("org_id=?", query.OrgId)
-
-	if query.Query != "" {
-		sess.Where("name LIKE ?", queryWithWildcards)
-	}
-	if query.Name != "" {
-		sess.Where("name=?", query.Name)
-	}
-	sess.Asc("name")
-
-	offset := query.Limit * (query.Page - 1)
-	sess.Limit(query.Limit, offset)
-	sess.Cols("id", "name")
-	if err := sess.Find(&query.Result.UserGroups); err != nil {
-		return err
-	}
-
-	userGroup := m.UserGroup{}
-
-	countSess := x.Table("user_group")
-	if query.Query != "" {
-		countSess.Where("name LIKE ?", queryWithWildcards)
-	}
-	if query.Name != "" {
-		countSess.Where("name=?", query.Name)
-	}
-	count, err := countSess.Count(&userGroup)
-	query.Result.TotalCount = count
-
-	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 GetUserGroupsByUser(query *m.GetUserGroupsByUserQuery) error {
-	query.Result = make([]*m.UserGroup, 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)
-
-	err := sess.Find(&query.Result)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func AddUserGroupMember(cmd *m.AddUserGroupMemberCommand) 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 {
-			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 *DBSession) 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.login", "user.email")
-
-	err := sess.Find(&query.Result)
-	return err
-}

+ 0 - 114
pkg/services/sqlstore/user_group_test.go

@@ -1,114 +0,0 @@
-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", Page: 1, Limit: 10}
-				err = SearchUserGroups(query)
-				So(err, ShouldBeNil)
-				So(query.Page, ShouldEqual, 1)
-
-				userGroup1 := query.Result.UserGroups[0]
-				So(userGroup1.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", Page: 1}
-				err = SearchUserGroups(query)
-				So(err, ShouldBeNil)
-				So(len(query.Result.UserGroups), ShouldEqual, 2)
-				So(query.Result.TotalCount, ShouldEqual, 2)
-
-				query2 := &m.SearchUserGroupsQuery{Query: ""}
-				err = SearchUserGroups(query2)
-				So(err, ShouldBeNil)
-				So(len(query2.Result.UserGroups), ShouldEqual, 2)
-			})
-
-			Convey("Should be able to return all user groups a user is member of", func() {
-				groupId := group2.Result.Id
-				err := AddUserGroupMember(&m.AddUserGroupMemberCommand{OrgId: 1, UserGroupId: groupId, UserId: userIds[0]})
-
-				query := &m.GetUserGroupsByUserQuery{UserId: userIds[0]}
-				err = GetUserGroupsByUser(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]})
-				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 and permissions", 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 = SetDashboardAcl(&m.SetDashboardAclCommand{DashboardId: 1, OrgId: 1, Permission: m.PERMISSION_EDIT, UserGroupId: groupId})
-
-				err = DeleteUserGroup(&m.DeleteUserGroupCommand{Id: groupId})
-				So(err, ShouldBeNil)
-
-				query := &m.GetUserGroupByIdQuery{Id: groupId}
-				err = GetUserGroupById(query)
-				So(err, ShouldEqual, m.ErrUserGroupNotFound)
-
-				permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: 1}
-				err = GetDashboardAclInfoList(permQuery)
-				So(err, ShouldBeNil)
-
-				So(len(permQuery.Result), ShouldEqual, 0)
-			})
-		})
-	})
-}

+ 10 - 10
public/app/core/components/user_group_picker.ts → public/app/core/components/team_picker.ts

@@ -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);

+ 2 - 2
public/app/core/core.ts

@@ -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,

+ 6 - 6
public/app/core/routes/routes.ts

@@ -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,
   })

+ 3 - 3
public/app/features/dashboard/acl/acl.html

@@ -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"> -->

+ 11 - 11
public/app/features/dashboard/acl/acl.ts

@@ -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;

+ 14 - 14
public/app/features/dashboard/acl/specs/acl_specs.ts

@@ -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', () => {

+ 3 - 1
public/app/features/org/all.ts

@@ -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';

+ 37 - 0
public/app/features/org/create_team_modal.ts

@@ -0,0 +1,37 @@
+///<reference path="../../headers/common.d.ts" />
+
+import coreModule from 'app/core/core_module';
+import appEvents from 'app/core/app_events';
+
+export class CreateTeamCtrl {
+  teamName = '';
+
+  /** @ngInject */
+  constructor(private backendSrv, private $location) {
+  }
+
+  createTeam() {
+    this.backendSrv.post('/api/teams', {name: this.teamName}).then((result) => {
+      if (result.teamId) {
+        this.$location.path('/org/teams/edit/' + result.teamId);
+      }
+      this.dismiss();
+    });
+  }
+
+  dismiss() {
+    appEvents.emit('hide-modal');
+  }
+}
+
+export function createTeamModal() {
+  return {
+    restrict: 'E',
+    templateUrl: 'public/app/features/org/partials/create_team.html',
+    controller: CreateTeamCtrl,
+    bindToController: true,
+    controllerAs: 'ctrl',
+  };
+}
+
+coreModule.directive('createTeamModal', createTeamModal);

+ 0 - 37
public/app/features/org/create_user_group_modal.ts

@@ -1,37 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import coreModule from 'app/core/core_module';
-import appEvents from 'app/core/app_events';
-
-export class CreateUserGroupCtrl {
-  userGroupName = '';
-
-  /** @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);
-      }
-      this.dismiss();
-    });
-  }
-
-  dismiss() {
-    appEvents.emit('hide-modal');
-  }
-}
-
-export function createUserGroupModal() {
-  return {
-    restrict: 'E',
-    templateUrl: 'public/app/features/org/partials/create_user_group.html',
-    controller: CreateUserGroupCtrl,
-    bindToController: true,
-    controllerAs: 'ctrl',
-  };
-}
-
-coreModule.directive('createUserGroupModal', createUserGroupModal);

+ 4 - 4
public/app/features/org/partials/create_user_group.html → public/app/features/org/partials/create_team.html

@@ -1,7 +1,7 @@
 <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>

+ 9 - 9
public/app/features/org/partials/user_group_details.html → public/app/features/org/partials/team_details.html

@@ -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>

+ 9 - 9
public/app/features/org/partials/user_groups.html → public/app/features/org/partials/teams.html

@@ -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>

+ 8 - 8
public/app/features/org/specs/user_group_details_ctrl_specs.ts → public/app/features/org/specs/team_details_ctrl_specs.ts

@@ -1,8 +1,8 @@
-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');
     });
   });
 });

+ 77 - 0
public/app/features/org/team_details_ctrl.ts

@@ -0,0 +1,77 @@
+///<reference path="../../headers/common.d.ts" />
+
+import coreModule from 'app/core/core_module';
+
+export default class TeamDetailsCtrl {
+  team: Team;
+  teamMembers: User[] = [];
+  navModel: any;
+
+  constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) {
+    this.navModel = navModelSrv.getNav('cfg', 'users');
+    this.get();
+  }
+
+  get() {
+    if (this.$routeParams && this.$routeParams.id) {
+      this.backendSrv.get(`/api/teams/${this.$routeParams.id}`)
+        .then(result => {
+          this.team = result;
+        });
+      this.backendSrv.get(`/api/teams/${this.$routeParams.id}/members`)
+        .then(result => {
+          this.teamMembers = result;
+        });
+    }
+  }
+
+  removeTeamMember(teamMember: TeamMember) {
+    this.$scope.appEvent('confirm-modal', {
+      title: 'Remove Member',
+      text: 'Are you sure you want to remove ' + teamMember.name + ' from this group?',
+      yesText: "Remove",
+      icon: "fa-warning",
+      onConfirm: () => {
+        this.removeMemberConfirmed(teamMember);
+      }
+    });
+  }
+
+  removeMemberConfirmed(teamMember: TeamMember) {
+    this.backendSrv.delete(`/api/teams/${this.$routeParams.id}/members/${teamMember.userId}`)
+      .then(this.get.bind(this));
+  }
+
+  update() {
+    if (!this.$scope.teamDetailsForm.$valid) { return; }
+
+    this.backendSrv.put('/api/teams/' + this.team.id, {name: this.team.name});
+  }
+
+  userPicked(user) {
+    this.backendSrv.post(`/api/teams/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
+      this.$scope.$broadcast('user-picker-reset');
+      this.get();
+    });
+  }
+}
+
+export interface Team {
+  id: number;
+  name: string;
+}
+
+export interface User {
+  id: number;
+  name: string;
+  login: string;
+  email: string;
+}
+
+export interface TeamMember {
+  userId: number;
+  name: string;
+}
+
+coreModule.controller('TeamDetailsCtrl', TeamDetailsCtrl);
+

+ 12 - 12
public/app/features/org/user_groups_ctrl.ts → public/app/features/org/teams_ctrl.ts

@@ -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);

+ 0 - 77
public/app/features/org/user_group_details_ctrl.ts

@@ -1,77 +0,0 @@
-///<reference path="../../headers/common.d.ts" />
-
-import coreModule from 'app/core/core_module';
-
-export default class UserGroupDetailsCtrl {
-  userGroup: UserGroup;
-  userGroupMembers: User[] = [];
-  navModel: any;
-
-  constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) {
-    this.navModel = navModelSrv.getNav('cfg', 'users');
-    this.get();
-  }
-
-  get() {
-    if (this.$routeParams && this.$routeParams.id) {
-      this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}`)
-        .then(result => {
-          this.userGroup = result;
-        });
-      this.backendSrv.get(`/api/user-groups/${this.$routeParams.id}/members`)
-        .then(result => {
-          this.userGroupMembers = result;
-        });
-    }
-  }
-
-  removeUserGroupMember(userGroupMember: UserGroupMember) {
-    this.$scope.appEvent('confirm-modal', {
-      title: 'Remove Member',
-      text: 'Are you sure you want to remove ' + userGroupMember.name + ' from this group?',
-      yesText: "Remove",
-      icon: "fa-warning",
-      onConfirm: () => {
-        this.removeMemberConfirmed(userGroupMember);
-      }
-    });
-  }
-
-  removeMemberConfirmed(userGroupMember: UserGroupMember) {
-    this.backendSrv.delete(`/api/user-groups/${this.$routeParams.id}/members/${userGroupMember.userId}`)
-      .then(this.get.bind(this));
-  }
-
-  update() {
-    if (!this.$scope.userGroupDetailsForm.$valid) { return; }
-
-    this.backendSrv.put('/api/user-groups/' + this.userGroup.id, {name: this.userGroup.name});
-  }
-
-  userPicked(user) {
-    this.backendSrv.post(`/api/user-groups/${this.$routeParams.id}/members`, {userId: user.id}).then(() => {
-      this.$scope.$broadcast('user-picker-reset');
-      this.get();
-    });
-  }
-}
-
-export interface UserGroup {
-  id: number;
-  name: string;
-}
-
-export interface User {
-  id: number;
-  name: string;
-  login: string;
-  email: string;
-}
-
-export interface UserGroupMember {
-  userId: number;
-  name: string;
-}
-
-coreModule.controller('UserGroupDetailsCtrl', UserGroupDetailsCtrl);
-

+ 2 - 2
public/app/plugins/panel/permissionlist/module.html

@@ -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>

+ 2 - 2
public/app/plugins/panel/permissionlist/module.ts

@@ -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);
     });
   }

+ 0 - 0
public/img/icons_dark_theme/icon_user_group.svg → public/img/icons_dark_theme/icon_team.svg


+ 0 - 0
public/img/icons_light_theme/icon_user_group.svg → public/img/icons_light_theme/icon_team.svg


+ 2 - 2
public/sass/base/_icons.scss

@@ -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 {