mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
NOISSUE - Allow sorting (#3052)
Signed-off-by: Musilah <nataleigh.nk@gmail.com>
This commit is contained in:
+6
-4
@@ -28,10 +28,12 @@ const (
|
||||
LimitKey = "limit"
|
||||
OnlyTotal = "only_total"
|
||||
|
||||
NameOrder = "name"
|
||||
IDOrder = "id"
|
||||
AscDir = "asc"
|
||||
DescDir = "desc"
|
||||
NameOrder = "name"
|
||||
IDOrder = "id"
|
||||
AscDir = "asc"
|
||||
DescDir = "desc"
|
||||
UpdatedAtOrder = "updated_at"
|
||||
CreatedAtOrder = "created_at"
|
||||
|
||||
MetadataKey = "metadata"
|
||||
NameKey = "name"
|
||||
|
||||
@@ -93,6 +93,16 @@ func (req listChannelsReq) validate() error {
|
||||
return apiutil.ErrNameSize
|
||||
}
|
||||
|
||||
switch req.Order {
|
||||
case "", api.NameOrder, api.CreatedAtOrder, api.UpdatedAtOrder:
|
||||
default:
|
||||
return apiutil.ErrInvalidOrder
|
||||
}
|
||||
|
||||
if req.Dir != "" && (req.Dir != api.DescDir && req.Dir != api.AscDir) {
|
||||
return apiutil.ErrInvalidDirection
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,16 @@ func (req listClientsReq) validate() error {
|
||||
return apiutil.ErrNameSize
|
||||
}
|
||||
|
||||
switch req.Order {
|
||||
case "", api.NameOrder, api.CreatedAtOrder, api.UpdatedAtOrder:
|
||||
default:
|
||||
return apiutil.ErrInvalidOrder
|
||||
}
|
||||
|
||||
if req.Dir != "" && (req.Dir != api.DescDir && req.Dir != api.AscDir) {
|
||||
return apiutil.ErrInvalidDirection
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,16 @@ type listDomainsReq struct {
|
||||
}
|
||||
|
||||
func (req listDomainsReq) validate() error {
|
||||
switch req.Order {
|
||||
case "", api.NameOrder, api.CreatedAtOrder, api.UpdatedAtOrder:
|
||||
default:
|
||||
return apiutil.ErrInvalidOrder
|
||||
}
|
||||
|
||||
if req.Dir != "" && (req.Dir != api.DescDir && req.Dir != api.AscDir) {
|
||||
return apiutil.ErrInvalidDirection
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -284,6 +284,15 @@ func decodePageMeta(r *http.Request) (groups.PageMeta, error) {
|
||||
return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
order, err := apiutil.ReadStringQuery(r, api.OrderKey, api.DefOrder)
|
||||
if err != nil {
|
||||
return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
dir, err := apiutil.ReadStringQuery(r, api.DirKey, api.DescDir)
|
||||
if err != nil {
|
||||
return groups.PageMeta{}, errors.Wrap(apiutil.ErrValidation, err)
|
||||
}
|
||||
|
||||
ret := groups.PageMeta{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
@@ -297,6 +306,8 @@ func decodePageMeta(r *http.Request) (groups.PageMeta, error) {
|
||||
AccessType: accessType,
|
||||
RootGroup: rootGroup,
|
||||
OnlyTotal: ot,
|
||||
Order: order,
|
||||
Dir: dir,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ func TestDecodeListGroupsRequest(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Limit: 10,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
@@ -54,6 +56,8 @@ func TestDecodeListGroupsRequest(t *testing.T) {
|
||||
"test": "test",
|
||||
},
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
@@ -166,13 +170,15 @@ func TestDecodeListChildrenRequest(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Limit: 10,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
desc: "valid request with all parameters",
|
||||
url: "http://localhost:8080?status=enabled&offset=10&limit=10&name=random&metadata={\"test\":\"test\"}&level=2&parent_id=random&tree=true&dir=-1&member_kind=random&permission=random&list_perms=true",
|
||||
url: "http://localhost:8080?status=enabled&offset=10&limit=10&name=random&metadata={\"test\":\"test\"}&level=2&parent_id=random&tree=true&dir=desc&member_kind=random&permission=random&list_perms=true",
|
||||
header: map[string][]string{
|
||||
"Authorization": {"Bearer 123"},
|
||||
},
|
||||
@@ -188,6 +194,8 @@ func TestDecodeListChildrenRequest(t *testing.T) {
|
||||
"test": "test",
|
||||
},
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
@@ -227,6 +235,8 @@ func TestDecodePageMeta(t *testing.T) {
|
||||
resp: groups.PageMeta{
|
||||
Limit: 10,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
@@ -242,6 +252,8 @@ func TestDecodePageMeta(t *testing.T) {
|
||||
"test": "test",
|
||||
},
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
|
||||
@@ -2034,6 +2034,8 @@ func TestListChildrenGroupsEndpoint(t *testing.T) {
|
||||
Limit: 1,
|
||||
Offset: 0,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
svcRes: groups.Page{
|
||||
PageMeta: groups.PageMeta{
|
||||
@@ -2056,6 +2058,8 @@ func TestListChildrenGroupsEndpoint(t *testing.T) {
|
||||
Limit: 1,
|
||||
Offset: 0,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
authnErr: svcerr.ErrAuthentication,
|
||||
status: http.StatusUnauthorized,
|
||||
@@ -2072,6 +2076,8 @@ func TestListChildrenGroupsEndpoint(t *testing.T) {
|
||||
Limit: 1,
|
||||
Offset: 0,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
status: http.StatusUnauthorized,
|
||||
err: apiutil.ErrBearerToken,
|
||||
@@ -2094,6 +2100,8 @@ func TestListChildrenGroupsEndpoint(t *testing.T) {
|
||||
Limit: 1,
|
||||
Offset: 0,
|
||||
Actions: []string{},
|
||||
Dir: "desc",
|
||||
Order: "updated_at",
|
||||
},
|
||||
svcRes: groups.Page{},
|
||||
svcErr: svcerr.ErrAuthorization,
|
||||
|
||||
@@ -66,6 +66,17 @@ func (req listGroupsReq) validate() error {
|
||||
if req.userID != "" && req.groupID != "" {
|
||||
return apiutil.ErrMultipleEntitiesFilter
|
||||
}
|
||||
|
||||
switch req.Order {
|
||||
case "", api.NameOrder, api.CreatedAtOrder, api.UpdatedAtOrder:
|
||||
default:
|
||||
return apiutil.ErrInvalidOrder
|
||||
}
|
||||
|
||||
if req.Dir != "" && (req.Dir != api.DescDir && req.Dir != api.AscDir) {
|
||||
return apiutil.ErrInvalidDirection
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ type PageMeta struct {
|
||||
OnlyTotal bool `json:"only_total"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Dir string `json:"dir,omitempty"`
|
||||
Order string `json:"order,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
DomainID string `json:"domain_id,omitempty"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
api "github.com/absmach/supermq/api/http"
|
||||
groups "github.com/absmach/supermq/groups"
|
||||
"github.com/absmach/supermq/internal/nullable"
|
||||
"github.com/absmach/supermq/pkg/errors"
|
||||
@@ -408,11 +409,19 @@ func (repo groupRepository) RetrieveByIDAndUser(ctx context.Context, domainID, u
|
||||
}
|
||||
|
||||
func (repo groupRepository) RetrieveAll(ctx context.Context, pm groups.PageMeta) (groups.Page, error) {
|
||||
var q string
|
||||
query := buildQuery(pm)
|
||||
|
||||
q = fmt.Sprintf(`SELECT DISTINCT g.id, g.domain_id, tags, COALESCE(g.parent_id, '') AS parent_id, g.name, g.description,
|
||||
g.metadata, g.created_at, g.updated_at, g.updated_by, g.status FROM groups g %s ORDER BY g.created_at LIMIT :limit OFFSET :offset;`, query)
|
||||
orderClause := ""
|
||||
switch pm.Order {
|
||||
case "name", "created_at", "updated_at":
|
||||
orderClause = fmt.Sprintf("ORDER BY g.%s", pm.Order)
|
||||
if pm.Dir == api.AscDir || pm.Dir == api.DescDir {
|
||||
orderClause = fmt.Sprintf("%s %s", orderClause, pm.Dir)
|
||||
}
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`SELECT DISTINCT g.id, g.domain_id, tags, COALESCE(g.parent_id, '') AS parent_id, g.name, g.description,
|
||||
g.metadata, g.created_at, g.updated_at, g.updated_by, g.status FROM groups g %s %s LIMIT :limit OFFSET :offset;`, query, orderClause)
|
||||
|
||||
dbPageMeta, err := toDBGroupPageMeta(pm)
|
||||
if err != nil {
|
||||
|
||||
@@ -707,6 +707,8 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
@@ -725,6 +727,8 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 10,
|
||||
Limit: 10,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
@@ -743,6 +747,9 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 0,
|
||||
Limit: 50,
|
||||
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
@@ -761,6 +768,8 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 50,
|
||||
Limit: 50,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
@@ -779,6 +788,8 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 1000,
|
||||
Limit: 50,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
@@ -797,6 +808,8 @@ func TestRetrieveAll(t *testing.T) {
|
||||
PageMeta: groups.PageMeta{
|
||||
Offset: 170,
|
||||
Limit: 50,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
},
|
||||
},
|
||||
response: groups.Page{
|
||||
|
||||
+24
-8
@@ -322,12 +322,16 @@ func TestListGroups(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
pageMeta: sdk.PageMetadata{
|
||||
Offset: offset,
|
||||
Limit: 100,
|
||||
Offset: offset,
|
||||
Limit: 100,
|
||||
Order: "created_at",
|
||||
Direction: "asc",
|
||||
},
|
||||
svcReq: groups.PageMeta{
|
||||
Offset: offset,
|
||||
Limit: 100,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
Actions: []string{},
|
||||
},
|
||||
svcRes: groups.Page{
|
||||
@@ -381,12 +385,16 @@ func TestListGroups(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
pageMeta: sdk.PageMetadata{
|
||||
Offset: offset,
|
||||
Limit: 0,
|
||||
Offset: offset,
|
||||
Limit: 0,
|
||||
Order: "created_at",
|
||||
Direction: "asc",
|
||||
},
|
||||
svcReq: groups.PageMeta{
|
||||
Offset: offset,
|
||||
Limit: 10,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
Actions: []string{},
|
||||
},
|
||||
svcRes: groups.Page{
|
||||
@@ -423,8 +431,10 @@ func TestListGroups(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
pageMeta: sdk.PageMetadata{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Order: "created_at",
|
||||
Direction: "asc",
|
||||
Metadata: sdk.Metadata{
|
||||
"name": "user_89",
|
||||
},
|
||||
@@ -432,6 +442,8 @@ func TestListGroups(t *testing.T) {
|
||||
svcReq: groups.PageMeta{
|
||||
Offset: 0,
|
||||
Limit: 10,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
Metadata: groups.Metadata{
|
||||
"name": "user_89",
|
||||
},
|
||||
@@ -474,12 +486,16 @@ func TestListGroups(t *testing.T) {
|
||||
domainID: domainID,
|
||||
token: validToken,
|
||||
pageMeta: sdk.PageMetadata{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Order: "created_at",
|
||||
Direction: "asc",
|
||||
},
|
||||
svcReq: groups.PageMeta{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Order: "created_at",
|
||||
Dir: "asc",
|
||||
Actions: []string{},
|
||||
},
|
||||
svcRes: groups.Page{
|
||||
|
||||
@@ -743,7 +743,7 @@ func TestListUsers(t *testing.T) {
|
||||
},
|
||||
},
|
||||
token: validToken,
|
||||
query: "order=name",
|
||||
query: "order=username",
|
||||
status: http.StatusOK,
|
||||
authnRes: smqauthn.Session{UserID: validID, DomainID: domainID},
|
||||
err: nil,
|
||||
|
||||
@@ -99,6 +99,13 @@ func (req listUsersReq) validate() error {
|
||||
if req.limit > maxLimitSize || req.limit < 1 {
|
||||
return apiutil.ErrLimitSize
|
||||
}
|
||||
|
||||
switch req.order {
|
||||
case "", api.CreatedAtOrder, api.UpdatedAtOrder, api.FirstNameKey, api.LastNameKey, api.UsernameKey, api.EmailKey:
|
||||
default:
|
||||
return apiutil.ErrInvalidOrder
|
||||
}
|
||||
|
||||
if req.dir != "" && (req.dir != api.AscDir && req.dir != api.DescDir) {
|
||||
return apiutil.ErrInvalidDirection
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (res viewUserRes) Empty() bool {
|
||||
|
||||
type usersPageRes struct {
|
||||
pageRes
|
||||
Users []viewUserRes `json:"users,omitempty"`
|
||||
Users []viewUserRes `json:"users"`
|
||||
}
|
||||
|
||||
func (res usersPageRes) Code() int {
|
||||
|
||||
+10
-1
@@ -117,9 +117,18 @@ func (repo *userRepo) RetrieveAll(ctx context.Context, pm users.Page) (users.Use
|
||||
return users.UsersPage{}, errors.Wrap(repoerr.ErrViewEntity, err)
|
||||
}
|
||||
|
||||
orderClause := ""
|
||||
switch pm.Order {
|
||||
case "first_name", "last_name", "username", "email", "created_at", "updated_at":
|
||||
orderClause = fmt.Sprintf("ORDER BY u.%s", pm.Order)
|
||||
if pm.Dir == api.AscDir || pm.Dir == api.DescDir {
|
||||
orderClause = fmt.Sprintf("%s %s", orderClause, pm.Dir)
|
||||
}
|
||||
}
|
||||
|
||||
q := fmt.Sprintf(`SELECT u.id, u.tags, u.email, u.metadata, u.status, u.role, u.first_name, u.last_name, u.username,
|
||||
u.created_at, u.updated_at, u.profile_picture, COALESCE(u.updated_by, '') AS updated_by
|
||||
FROM users u %s ORDER BY u.created_at LIMIT :limit OFFSET :offset;`, query)
|
||||
FROM users u %s %s LIMIT :limit OFFSET :offset;`, query, orderClause)
|
||||
|
||||
dbPage, err := ToDBUsersPage(pm)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user