User: Support sort query param for user and org user, search endpoints (#75229)
* User: Add sort option to user search * Switch to an approach that uses the dashboard search options * Cable user sort on the org endpoint * Alias user table with u in org store * Add test and cover orgs/:orgID/users/search endpoint * Add test to userimpl store * Simplify the store_test with sortopts.ParseSortQueryParam * Account for PR feedback * Positive check * Update docs * Update docs * Switch to ErrOrFallback Co-authored-by: Karl Persson <kalle.persson@grafana.com> --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
co-authored by
Karl Persson
parent
4563fc48af
commit
96cbe70b14
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers/sortopts"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
@@ -42,7 +43,7 @@ func ProvideUsersService(searchUserFilter user.SearchUserFilter, userService use
|
||||
func (s *OSSService) SearchUsers(c *contextmodel.ReqContext) response.Response {
|
||||
result, err := s.SearchUser(c)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to fetch users", err)
|
||||
return response.ErrOrFallback(500, "Failed to fetch users", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, result.Users)
|
||||
@@ -61,7 +62,7 @@ func (s *OSSService) SearchUsers(c *contextmodel.ReqContext) response.Response {
|
||||
func (s *OSSService) SearchUsersWithPaging(c *contextmodel.ReqContext) response.Response {
|
||||
result, err := s.SearchUser(c)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to fetch users", err)
|
||||
return response.ErrOrFallback(500, "Failed to fetch users", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, result)
|
||||
@@ -87,6 +88,11 @@ func (s *OSSService) SearchUser(c *contextmodel.ReqContext) (*user.SearchUserQue
|
||||
}
|
||||
}
|
||||
|
||||
sortOpts, err := sortopts.ParseSortQueryParam(c.Query("sort"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := &user.SearchUsersQuery{
|
||||
// added SignedInUser to the query, as to only list the users that the user has permission to read
|
||||
SignedInUser: c.SignedInUser,
|
||||
@@ -94,6 +100,7 @@ func (s *OSSService) SearchUser(c *contextmodel.ReqContext) (*user.SearchUserQue
|
||||
Filters: filters,
|
||||
Page: page,
|
||||
Limit: perPage,
|
||||
SortOpts: sortOpts,
|
||||
}
|
||||
res, err := s.userService.Search(c.Req.Context(), query)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package sortopts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var (
|
||||
// SortOptionsByQueryParam is a map to translate the "sort" query param values to SortOption(s)
|
||||
SortOptionsByQueryParam = map[string]model.SortOption{
|
||||
"login-asc": newSortOption("login", false, 0),
|
||||
"login-desc": newSortOption("login", true, 0),
|
||||
"email-asc": newSortOption("email", false, 1),
|
||||
"email-desc": newSortOption("email", true, 1),
|
||||
"name-asc": newSortOption("name", false, 2),
|
||||
"name-desc": newSortOption("name", true, 2),
|
||||
"lastSeenAtAge-asc": newTimeSortOption("last_seen_at", false, 3),
|
||||
"lastSeenAtAge-desc": newTimeSortOption("last_seen_at", true, 3),
|
||||
}
|
||||
|
||||
ErrorUnknownSortingOption = errutil.BadRequest("unknown sorting option")
|
||||
)
|
||||
|
||||
type Sorter struct {
|
||||
Field string
|
||||
Descending bool
|
||||
}
|
||||
|
||||
func (s Sorter) OrderBy() string {
|
||||
if s.Descending {
|
||||
return fmt.Sprintf("u.%v DESC", s.Field)
|
||||
}
|
||||
return fmt.Sprintf("u.%v ASC", s.Field)
|
||||
}
|
||||
|
||||
func newSortOption(field string, desc bool, index int) model.SortOption {
|
||||
direction := "asc"
|
||||
description := ("A-Z")
|
||||
if desc {
|
||||
direction = "desc"
|
||||
description = ("Z-A")
|
||||
}
|
||||
return model.SortOption{
|
||||
Name: fmt.Sprintf("%v-%v", field, direction),
|
||||
DisplayName: fmt.Sprintf("%v (%v)", cases.Title(language.Und).String(field), description),
|
||||
Description: fmt.Sprintf("Sort %v in an alphabetically %vending order", field, direction),
|
||||
Index: index,
|
||||
Filter: []model.SortOptionFilter{Sorter{Field: field, Descending: desc}},
|
||||
}
|
||||
}
|
||||
|
||||
func newTimeSortOption(field string, desc bool, index int) model.SortOption {
|
||||
direction := "asc"
|
||||
description := ("Oldest-Newest")
|
||||
if desc {
|
||||
direction = "desc"
|
||||
description = ("Newest-Oldest")
|
||||
}
|
||||
return model.SortOption{
|
||||
Name: fmt.Sprintf("%v-%v", field, direction),
|
||||
DisplayName: fmt.Sprintf("%v (%v)", cases.Title(language.Und).String(field), description),
|
||||
Description: fmt.Sprintf("Sort %v in an alphabetically %vending order", field, direction),
|
||||
Index: index,
|
||||
Filter: []model.SortOptionFilter{Sorter{Field: field, Descending: desc}},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseSortQueryParam parses the "sort" query param and returns an ordered list of SortOption(s)
|
||||
func ParseSortQueryParam(param string) ([]model.SortOption, error) {
|
||||
opts := []model.SortOption{}
|
||||
if param != "" {
|
||||
optsStr := strings.Split(param, ",")
|
||||
for i := range optsStr {
|
||||
if opt, ok := SortOptionsByQueryParam[optsStr[i]]; !ok {
|
||||
return nil, ErrorUnknownSortingOption.Errorf("%v option unknown", optsStr[i])
|
||||
} else {
|
||||
opts = append(opts, opt)
|
||||
}
|
||||
}
|
||||
sort.Slice(opts, func(i, j int) bool {
|
||||
return opts[i].Index < opts[j].Index || (opts[i].Index == opts[j].Index && opts[i].Name < opts[j].Name)
|
||||
})
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
Reference in New Issue
Block a user