mirror of https://github.com/grafana/grafana
parent
af67aea2a9
commit
233cd7af4a
@ -0,0 +1,66 @@ |
|||||||
|
package api |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/grafana/grafana/pkg/bus" |
||||||
|
"github.com/grafana/grafana/pkg/metrics" |
||||||
|
"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) |
||||||
|
} |
||||||
|
|
||||||
|
metrics.M_Api_UserGroup_Create.Inc(1) |
||||||
|
|
||||||
|
return Json(200, &util.DynMap{ |
||||||
|
"userGroupId": cmd.Result.Id, |
||||||
|
"message": "User Group created", |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// 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, |
||||||
|
} |
||||||
|
|
||||||
|
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) |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
package api |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus" |
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson" |
||||||
|
"github.com/grafana/grafana/pkg/models" |
||||||
|
|
||||||
|
. "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"}, |
||||||
|
}, |
||||||
|
TotalCount: 2, |
||||||
|
} |
||||||
|
|
||||||
|
Convey("When searching with no parameters", func() { |
||||||
|
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) { |
||||||
|
var sentLimit int |
||||||
|
var sendPage int |
||||||
|
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error { |
||||||
|
query.Result = mockResult |
||||||
|
|
||||||
|
sentLimit = query.Limit |
||||||
|
sendPage = query.Page |
||||||
|
|
||||||
|
return nil |
||||||
|
}) |
||||||
|
|
||||||
|
sc.handlerFunc = SearchUserGroups |
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() |
||||||
|
|
||||||
|
So(sentLimit, ShouldEqual, 1000) |
||||||
|
So(sendPage, ShouldEqual, 1) |
||||||
|
|
||||||
|
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes()) |
||||||
|
So(err, ShouldBeNil) |
||||||
|
|
||||||
|
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2) |
||||||
|
So(len(respJSON.Get("userGroups").MustArray()), ShouldEqual, 2) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
Convey("When searching with page and perpage parameters", func() { |
||||||
|
loggedInUserScenario("When calling GET on", "/api/user-groups/search", func(sc *scenarioContext) { |
||||||
|
var sentLimit int |
||||||
|
var sendPage int |
||||||
|
bus.AddHandler("test", func(query *models.SearchUserGroupsQuery) error { |
||||||
|
query.Result = mockResult |
||||||
|
|
||||||
|
sentLimit = query.Limit |
||||||
|
sendPage = query.Page |
||||||
|
|
||||||
|
return nil |
||||||
|
}) |
||||||
|
|
||||||
|
sc.handlerFunc = SearchUserGroups |
||||||
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec() |
||||||
|
|
||||||
|
So(sentLimit, ShouldEqual, 10) |
||||||
|
So(sendPage, ShouldEqual, 2) |
||||||
|
}) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
<navbar icon="icon-gf icon-gf-users" title="User Groups" title-url="org"> |
||||||
|
</navbar> |
||||||
|
|
||||||
|
<div class="page-container"> |
||||||
|
<div class="page-header"> |
||||||
|
<h1>User Groups</h1> |
||||||
|
|
||||||
|
<div class="page-header-tabs"> |
||||||
|
<a class="btn btn-success" href="/org/user-groups/create"> |
||||||
|
<i class="fa fa-plus"></i> |
||||||
|
Create User Group |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="search-field-wrapper pull-right width-18"> |
||||||
|
<span style="position: relative;"> |
||||||
|
<input type="text" placeholder="Find User Group by name" tabindex="1" give-focus="true" |
||||||
|
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.get()" /> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div class="admin-list-table"> |
||||||
|
<table class="filter-table form-inline"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th>Id</th> |
||||||
|
<th>Name</th> |
||||||
|
<th></th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody> |
||||||
|
<tr ng-repeat="userGroup in ctrl.userGroups"> |
||||||
|
<td>{{userGroup.id}}</td> |
||||||
|
<td>{{userGroup.name}}</td> |
||||||
|
<td class="text-right"> |
||||||
|
<a href="org/user-groups/edit/{{userGroup.id}}" class="btn btn-inverse btn-small"> |
||||||
|
<i class="fa fa-edit"></i> |
||||||
|
Edit |
||||||
|
</a> |
||||||
|
|
||||||
|
<a ng-click="ctrl.deleteUserGroup(userGroup)" class="btn btn-danger btn-small"> |
||||||
|
<i class="fa fa-remove"></i> |
||||||
|
</a> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
|
||||||
|
</table> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="admin-list-paging" ng-if="ctrl.showPaging"> |
||||||
|
<ol> |
||||||
|
<li ng-repeat="page in ctrl.pages"> |
||||||
|
<button |
||||||
|
class="btn btn-small" |
||||||
|
ng-class="{'btn-secondary': page.current, 'btn-inverse': !page.current}" |
||||||
|
ng-click="ctrl.navigateToPage(page)">{{page.page}}</button> |
||||||
|
</li> |
||||||
|
</ol> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,68 @@ |
|||||||
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
|
import coreModule from 'app/core/core_module'; |
||||||
|
|
||||||
|
export default class UserGroupsCtrl { |
||||||
|
userGroups: any; |
||||||
|
pages = []; |
||||||
|
perPage = 50; |
||||||
|
page = 1; |
||||||
|
totalPages: number; |
||||||
|
showPaging = false; |
||||||
|
query: any = ''; |
||||||
|
|
||||||
|
/** @ngInject */ |
||||||
|
constructor(private $scope, private $http, private backendSrv) { |
||||||
|
this.get(); |
||||||
|
} |
||||||
|
|
||||||
|
get() { |
||||||
|
this.backendSrv.get(`/api/user-groups/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`) |
||||||
|
.then((result) => { |
||||||
|
this.userGroups = result.userGroups; |
||||||
|
this.page = result.page; |
||||||
|
this.perPage = result.perPage; |
||||||
|
this.totalPages = Math.ceil(result.totalCount / result.perPage); |
||||||
|
this.showPaging = this.totalPages > 1; |
||||||
|
this.pages = []; |
||||||
|
|
||||||
|
for (var i = 1; i < this.totalPages+1; i++) { |
||||||
|
this.pages.push({ page: i, current: i === this.page}); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
navigateToPage(page) { |
||||||
|
this.page = page.page; |
||||||
|
this.get(); |
||||||
|
} |
||||||
|
|
||||||
|
deleteUserGroup(userGroup) { |
||||||
|
this.$scope.appEvent('confirm-modal', { |
||||||
|
title: 'Delete', |
||||||
|
text: 'Are you sure you want to delete User Group ' + userGroup.name + '?', |
||||||
|
yesText: "Delete", |
||||||
|
icon: "fa-warning", |
||||||
|
onConfirm: () => { |
||||||
|
this.deleteUserGroupConfirmed(userGroup); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
deleteUserGroupConfirmed(userGroup) { |
||||||
|
this.backendSrv.delete('/api/user-groups/' + userGroup.id) |
||||||
|
.then(this.get.bind(this)); |
||||||
|
} |
||||||
|
|
||||||
|
openUserGroupModal() { |
||||||
|
var modalScope = this.$scope.$new(); |
||||||
|
|
||||||
|
this.$scope.appEvent('show-modal', { |
||||||
|
src: 'public/app/features/org/partials/add_user.html', |
||||||
|
modalClass: 'user-group-modal', |
||||||
|
scope: modalScope |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
coreModule.controller('UserGroupsCtrl', UserGroupsCtrl); |
Loading…
Reference in new issue