mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:14 -04:00
[management] Groups API with name query parameter (#4831)
This commit is contained in:
@@ -48,6 +48,29 @@ func (h *handler) getAllGroups(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
accountID, userID := userAuth.AccountId, userAuth.UserId
|
||||
|
||||
// Check if filtering by name
|
||||
groupName := r.URL.Query().Get("name")
|
||||
if groupName != "" {
|
||||
// Get single group by name
|
||||
group, err := h.accountManager.GetGroupByName(r.Context(), groupName, accountID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
accountPeers, err := h.accountManager.GetPeers(r.Context(), accountID, userID, "", "")
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
// Return as array with single element to maintain API consistency
|
||||
groupsResponse := []*api.Group{toGroupResponse(accountPeers, group)}
|
||||
util.WriteJSONObject(r.Context(), w, groupsResponse)
|
||||
return
|
||||
}
|
||||
|
||||
// Get all groups
|
||||
groups, err := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
|
||||
@@ -60,12 +60,23 @@ func initGroupTestData(initGroups ...*types.Group) *handler {
|
||||
|
||||
return group, nil
|
||||
},
|
||||
GetAllGroupsFunc: func(ctx context.Context, accountID, userID string) ([]*types.Group, error) {
|
||||
groups := []*types.Group{
|
||||
{ID: "id-jwt-group", Name: "From JWT", Issued: types.GroupIssuedJWT},
|
||||
{ID: "id-existed", Name: "Existed", Peers: []string{"A", "B"}, Issued: types.GroupIssuedAPI},
|
||||
{ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI},
|
||||
}
|
||||
|
||||
groups = append(groups, initGroups...)
|
||||
|
||||
return groups, nil
|
||||
},
|
||||
GetGroupByNameFunc: func(ctx context.Context, groupName, _ string) (*types.Group, error) {
|
||||
if groupName == "All" {
|
||||
return &types.Group{ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown group name")
|
||||
return nil, status.Errorf(status.NotFound, "unknown group name")
|
||||
},
|
||||
GetPeersFunc: func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
|
||||
return maps.Values(TestPeers), nil
|
||||
@@ -287,6 +298,84 @@ func TestWriteGroup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllGroups(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
expectedStatus int
|
||||
expectedBody bool
|
||||
requestType string
|
||||
requestPath string
|
||||
expectedCount int
|
||||
}{
|
||||
{
|
||||
name: "Get All Groups",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/groups",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedCount: 3, // id-jwt-group, id-existed, id-all
|
||||
},
|
||||
{
|
||||
name: "Get Group By Name - Existing",
|
||||
expectedBody: true,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/groups?name=All",
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedCount: 1,
|
||||
},
|
||||
{
|
||||
name: "Get Group By Name - Not Found",
|
||||
expectedBody: false,
|
||||
requestType: http.MethodGet,
|
||||
requestPath: "/api/groups?name=NonExistent",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
p := initGroupTestData()
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
|
||||
req = nbcontext.SetUserAuthInRequest(req, auth.UserAuth{
|
||||
UserId: "test_user",
|
||||
Domain: "hotmail.com",
|
||||
AccountId: "test_id",
|
||||
})
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/api/groups", p.getAllGroups).Methods("GET")
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
res := recorder.Result()
|
||||
defer res.Body.Close()
|
||||
|
||||
if status := recorder.Code; status != tc.expectedStatus {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||
status, tc.expectedStatus)
|
||||
return
|
||||
}
|
||||
|
||||
if !tc.expectedBody {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read response body: %v", err)
|
||||
}
|
||||
|
||||
var groups []api.Group
|
||||
if err = json.Unmarshal(content, &groups); err != nil {
|
||||
t.Fatalf("Response is not in correct json format; %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, tc.expectedCount, len(groups))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteGroup(t *testing.T) {
|
||||
tt := []struct {
|
||||
name string
|
||||
|
||||
@@ -4,10 +4,14 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
)
|
||||
|
||||
// ErrGroupNotFound is returned when a group is not found
|
||||
var ErrGroupNotFound = errors.New("group not found")
|
||||
|
||||
// GroupsAPI APIs for Groups, do not use directly
|
||||
type GroupsAPI struct {
|
||||
c *Client
|
||||
@@ -27,6 +31,27 @@ func (a *GroupsAPI) List(ctx context.Context) ([]api.Group, error) {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// GetByName get group by name
|
||||
// See more: https://docs.netbird.io/api/resources/groups#list-all-groups
|
||||
func (a *GroupsAPI) GetByName(ctx context.Context, groupName string) (*api.Group, error) {
|
||||
params := map[string]string{"name": groupName}
|
||||
resp, err := a.c.NewRequest(ctx, "GET", "/api/groups", nil, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
ret, err := parseResponse[[]api.Group](resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret) == 0 {
|
||||
return nil, ErrGroupNotFound
|
||||
}
|
||||
return &ret[0], nil
|
||||
}
|
||||
|
||||
// Get get group info
|
||||
// See more: https://docs.netbird.io/api/resources/groups#retrieve-a-group
|
||||
func (a *GroupsAPI) Get(ctx context.Context, groupID string) (*api.Group, error) {
|
||||
|
||||
@@ -3362,6 +3362,14 @@ paths:
|
||||
security:
|
||||
- BearerAuth: [ ]
|
||||
- TokenAuth: [ ]
|
||||
parameters:
|
||||
- in: query
|
||||
name: name
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Filter groups by name (exact match)
|
||||
example: "devs"
|
||||
responses:
|
||||
'200':
|
||||
description: A JSON Array of Groups
|
||||
@@ -3375,6 +3383,8 @@ paths:
|
||||
"$ref": "#/components/responses/bad_request"
|
||||
'401':
|
||||
"$ref": "#/components/responses/requires_authentication"
|
||||
'404':
|
||||
"$ref": "#/components/responses/not_found"
|
||||
'403':
|
||||
"$ref": "#/components/responses/forbidden"
|
||||
'500':
|
||||
|
||||
@@ -1908,6 +1908,12 @@ type GetApiEventsNetworkTrafficParamsConnectionType string
|
||||
// GetApiEventsNetworkTrafficParamsDirection defines parameters for GetApiEventsNetworkTraffic.
|
||||
type GetApiEventsNetworkTrafficParamsDirection string
|
||||
|
||||
// GetApiGroupsParams defines parameters for GetApiGroups.
|
||||
type GetApiGroupsParams struct {
|
||||
// Name Filter groups by name (exact match)
|
||||
Name *string `form:"name,omitempty" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// GetApiPeersParams defines parameters for GetApiPeers.
|
||||
type GetApiPeersParams struct {
|
||||
// Name Filter peers by name
|
||||
|
||||
Reference in New Issue
Block a user