Add sorting to GraphAPI users and groups

The GraphAPI endpoints for users and groups support ordering now.
User can be ordered by displayName, onPremisesSamAccountName and mail.
Groups can be ordered by displayName.

Example: https://localhost:9200/graph/v1.0/groups?$orderby=displayName asc

Closes #3360
This commit is contained in:
Ralf Haferkamp
2022-03-29 11:47:38 +02:00
parent c19144c703
commit c29842e697
4 changed files with 150 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
Enhancement: Add sorting to GraphAPI users and groups
The GraphAPI endpoints for users and groups support ordering now.
User can be ordered by displayName, onPremisesSamAccountName and mail.
Groups can be ordered by displayName.
Example:
https://localhost:9200/graph/v1.0/groups?$orderby=displayName asc
https://github.com/owncloud/ocis/issues/3360
+40
View File
@@ -6,8 +6,10 @@ import (
"fmt"
"net/http"
"net/url"
"sort"
"strings"
"github.com/CiscoM31/godata"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
@@ -19,6 +21,14 @@ const memberRefsLimit = 20
// GetGroups implements the Service interface.
func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
if err != nil {
g.logger.Err(err).Interface("query", r.URL.Query()).Msg("query error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
groups, err := g.identityBackend.GetGroups(r.Context(), r.URL.Query())
if err != nil {
@@ -29,6 +39,17 @@ func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
}
}
groups, err = sortGroups(odataReq, groups)
if err != nil {
var errcode errorcode.Error
if errors.As(err, &errcode) {
errcode.Render(w, r)
} else {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
}
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &listResponse{Value: groups})
}
@@ -318,3 +339,22 @@ func (g Graph) parseMemberRef(ref string) (string, string, error) {
memberType := segments[len(segments)-2]
return memberType, id, nil
}
func sortGroups(req *godata.GoDataRequest, groups []*libregraph.Group) ([]*libregraph.Group, error) {
var sorter sort.Interface
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
return groups, nil
}
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
case "displayName":
sorter = groupsByDisplayName{groups}
default:
return nil, fmt.Errorf("we do not support <%s> as a order parameter", req.Query.OrderBy.OrderByItems[0].Field.Value)
}
if req.Query.OrderBy.OrderByItems[0].Order == "asc" {
sorter = sort.Reverse(sorter)
}
sort.Sort(sorter)
return groups, nil
}
+56
View File
@@ -45,3 +45,59 @@ func (s spacesByLastModifiedDateTime) Less(i, j int) bool {
// fallback to name if no dateTime is set on both items
return strings.ToLower(*s.spacesSlice[i].Name) > strings.ToLower(*s.spacesSlice[j].Name)
}
type userSlice []*libregraph.User
// Len is the number of elements in the collection.
func (d userSlice) Len() int { return len(d) }
// Swap swaps the elements with indexes i and j.
func (d userSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
type usersByDisplayName struct {
userSlice
}
type usersByMail struct {
userSlice
}
type usersByOnPremisesSamAccountName struct {
userSlice
}
// Less reports whether the element with index i
// must sort before the element with index j.
func (u usersByDisplayName) Less(i, j int) bool {
return strings.ToLower(u.userSlice[i].GetDisplayName()) > strings.ToLower(u.userSlice[j].GetDisplayName())
}
// Less reports whether the element with index i
// must sort before the element with index j.
func (u usersByMail) Less(i, j int) bool {
return strings.ToLower(u.userSlice[i].GetMail()) > strings.ToLower(u.userSlice[j].GetMail())
}
// Less reports whether the element with index i
// must sort before the element with index j.
func (u usersByOnPremisesSamAccountName) Less(i, j int) bool {
return strings.ToLower(u.userSlice[i].GetOnPremisesSamAccountName()) > strings.ToLower(u.userSlice[j].GetOnPremisesSamAccountName())
}
type groupSlice []*libregraph.Group
// Len is the number of elements in the collection.
func (d groupSlice) Len() int { return len(d) }
// Swap swaps the elements with indexes i and j.
func (d groupSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
type groupsByDisplayName struct {
groupSlice
}
// Less reports whether the element with index i
// must sort before the element with index j.
func (g groupsByDisplayName) Less(i, j int) bool {
return strings.ToLower(g.groupSlice[i].GetDisplayName()) > strings.ToLower(g.groupSlice[j].GetDisplayName())
}
+44
View File
@@ -7,7 +7,10 @@ import (
"net/http"
"net/url"
"regexp"
"sort"
"strings"
"github.com/CiscoM31/godata"
revactx "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
@@ -38,6 +41,13 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
// GetUsers implements the Service interface.
func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
if err != nil {
g.logger.Err(err).Interface("query", r.URL.Query()).Msg("query error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
users, err := g.identityBackend.GetUsers(r.Context(), r.URL.Query())
if err != nil {
var errcode errorcode.Error
@@ -48,6 +58,17 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
}
return
}
users, err = sortUsers(odataReq, users)
if err != nil {
var errcode errorcode.Error
if errors.As(err, &errcode) {
errcode.Render(w, r)
} else {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
}
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &listResponse{Value: users})
}
@@ -234,3 +255,26 @@ func isValidEmail(e string) bool {
}
return emailRegex.MatchString(e)
}
func sortUsers(req *godata.GoDataRequest, users []*libregraph.User) ([]*libregraph.User, error) {
var sorter sort.Interface
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
return users, nil
}
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
case "displayName":
sorter = usersByDisplayName{users}
case "mail":
sorter = usersByMail{users}
case "onPremisesSamAccountName":
sorter = usersByOnPremisesSamAccountName{users}
default:
return nil, fmt.Errorf("we do not support <%s> as a order parameter", req.Query.OrderBy.OrderByItems[0].Field.Value)
}
if req.Query.OrderBy.OrderByItems[0].Order == "asc" {
sorter = sort.Reverse(sorter)
}
sort.Sort(sorter)
return users, nil
}