3
0

Fix pagination cursor dec/enc + finish impl

This commit is contained in:
Denis Arh
2020-10-12 15:20:55 +02:00
parent 56422397cc
commit b534308143
8 changed files with 128 additions and 113 deletions

View File

@@ -505,15 +505,9 @@ endpoints:
- type: uint
name: limit
title: Limit
- type: uint
name: offset
title: Offset
- type: uint
name: page
title: Page number (1-based)
- type: uint
name: perPage
title: Returned items per page (default 50)
- type: string
name: pageCursor
title: Page cursor
- type: string
name: sort
title: Sort items
@@ -903,15 +897,9 @@ endpoints:
- type: uint
name: limit
title: Limit
- type: uint
name: offset
title: Offset
- type: uint
name: page
title: Page number (1-based)
- type: uint
name: perPage
title: Returned items per page (default 50)
- type: string
name: pageCursor
title: Page cursor
- name: read
path: "/{attachmentID}"
method: GET

View File

@@ -51,8 +51,6 @@ func (ctrl Attachment) List(ctx context.Context, r *request.AttachmentList) (int
ModuleID: r.ModuleID,
RecordID: r.RecordID,
FieldName: r.FieldName,
//PageFilter: rh.Paging(r),
}
set, filter, err := ctrl.attachment.With(ctx).Find(f)

View File

@@ -78,14 +78,14 @@ func (ctrl *Record) List(ctx context.Context, r *request.RecordList) (interface{
m *types.Module
err error
rf = types.RecordFilter{
f = types.RecordFilter{
NamespaceID: r.NamespaceID,
ModuleID: r.ModuleID,
Deleted: filter.State(r.Deleted),
}
)
if err = rf.Sort.Set(r.Sort); err != nil {
if err = f.Sort.Set(r.Sort); err != nil {
return nil, err
}
@@ -95,14 +95,22 @@ func (ctrl *Record) List(ctx context.Context, r *request.RecordList) (interface{
if r.Query != "" {
// Query param takes preference
rf.Query = r.Query
f.Query = r.Query
} else if r.Filter != "" {
// Backward compatibility
// Filter param is deprecated
rf.Query = r.Filter
f.Query = r.Filter
}
rr, filter, err := ctrl.record.With(ctx).Find(rf)
if f.Paging, err = filter.NewPaging(r.Limit, r.PageCursor); err != nil {
return nil, err
}
if f.Sorting, err = filter.NewSorting(r.Sort); err != nil {
return nil, err
}
rr, filter, err := ctrl.record.With(ctx).Find(f)
return ctrl.makeFilterPayload(ctx, m, rr, &filter, err)
}

View File

@@ -75,20 +75,10 @@ type (
// Limit
Limit uint
// Offset GET parameter
// PageCursor GET parameter
//
// Offset
Offset uint
// Page GET parameter
//
// Page number (1-based)
Page uint
// PerPage GET parameter
//
// Returned items per page (default 50)
PerPage uint
// Page cursor
PageCursor string
}
AttachmentRead struct {
@@ -232,9 +222,7 @@ func (r AttachmentList) Auditable() map[string]interface{} {
"recordID": r.RecordID,
"fieldName": r.FieldName,
"limit": r.Limit,
"offset": r.Offset,
"page": r.Page,
"perPage": r.PerPage,
"pageCursor": r.PageCursor,
}
}
@@ -284,18 +272,8 @@ func (r AttachmentList) GetLimit() uint {
}
// Auditable returns all auditable/loggable parameters
func (r AttachmentList) GetOffset() uint {
return r.Offset
}
// Auditable returns all auditable/loggable parameters
func (r AttachmentList) GetPage() uint {
return r.Page
}
// Auditable returns all auditable/loggable parameters
func (r AttachmentList) GetPerPage() uint {
return r.PerPage
func (r AttachmentList) GetPageCursor() string {
return r.PageCursor
}
// Fill processes request and fills internal variables
@@ -357,20 +335,8 @@ func (r *AttachmentList) Fill(req *http.Request) (err error) {
return err
}
}
if val, ok := tmp["offset"]; ok && len(val) > 0 {
r.Offset, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["page"]; ok && len(val) > 0 {
r.Page, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["perPage"]; ok && len(val) > 0 {
r.PerPage, err = payload.ParseUint(val[0]), nil
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}

View File

@@ -88,20 +88,10 @@ type (
// Limit
Limit uint
// Offset GET parameter
// PageCursor GET parameter
//
// Offset
Offset uint
// Page GET parameter
//
// Page number (1-based)
Page uint
// PerPage GET parameter
//
// Returned items per page (default 50)
PerPage uint
// Page cursor
PageCursor string
// Sort GET parameter
//
@@ -519,9 +509,7 @@ func (r RecordList) Auditable() map[string]interface{} {
"filter": r.Filter,
"deleted": r.Deleted,
"limit": r.Limit,
"offset": r.Offset,
"page": r.Page,
"perPage": r.PerPage,
"pageCursor": r.PageCursor,
"sort": r.Sort,
}
}
@@ -557,18 +545,8 @@ func (r RecordList) GetLimit() uint {
}
// Auditable returns all auditable/loggable parameters
func (r RecordList) GetOffset() uint {
return r.Offset
}
// Auditable returns all auditable/loggable parameters
func (r RecordList) GetPage() uint {
return r.Page
}
// Auditable returns all auditable/loggable parameters
func (r RecordList) GetPerPage() uint {
return r.PerPage
func (r RecordList) GetPageCursor() string {
return r.PageCursor
}
// Auditable returns all auditable/loggable parameters
@@ -617,20 +595,8 @@ func (r *RecordList) Fill(req *http.Request) (err error) {
return err
}
}
if val, ok := tmp["offset"]; ok && len(val) > 0 {
r.Offset, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["page"]; ok && len(val) > 0 {
r.Page, err = payload.ParseUint(val[0]), nil
if err != nil {
return err
}
}
if val, ok := tmp["perPage"]; ok && len(val) > 0 {
r.PerPage, err = payload.ParseUint(val[0]), nil
if val, ok := tmp["pageCursor"]; ok && len(val) > 0 {
r.PageCursor, err = val[0], nil
if err != nil {
return err
}

View File

@@ -3,6 +3,7 @@ package types
import (
"database/sql/driver"
"encoding/json"
"github.com/cortezaproject/corteza-server/pkg/filter"
"time"
"github.com/pkg/errors"
@@ -42,6 +43,9 @@ type (
//
// Store then loads additional resources to satisfy the paging parameters
Check func(*Attachment) (bool, error)
// Standard helpers for paging and sorting
filter.Paging
}
attachmentImageMeta struct {

View File

@@ -85,7 +85,7 @@ func (p *PagingCursor) String() string {
}
func (p *PagingCursor) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
buf, err := json.Marshal(struct {
K []string
V []interface{}
D []bool
@@ -96,11 +96,26 @@ func (p *PagingCursor) MarshalJSON() ([]byte, error) {
p.desc,
p.Reverse,
})
if err != nil {
return nil, err
}
std := base64.StdEncoding
dbuf := make([]byte, std.EncodedLen(len(buf)))
std.Encode(dbuf, buf)
return append([]byte{'"'}, append(dbuf, '"')...), nil
}
func (p *PagingCursor) Encode() string {
b, _ := p.MarshalJSON()
return string(b)
}
func (p *PagingCursor) UnmarshalJSON(in []byte) error {
var (
aux *struct {
aux struct {
K []string
V []interface{}
D []bool
@@ -108,7 +123,7 @@ func (p *PagingCursor) UnmarshalJSON(in []byte) error {
}
)
if err := json.Unmarshal(in, aux); err != nil {
if err := json.Unmarshal(in, &aux); err != nil {
return err
}
@@ -120,6 +135,20 @@ func (p *PagingCursor) UnmarshalJSON(in []byte) error {
return nil
}
func (p *PagingCursor) Decode(cursor string) error {
b, err := base64.StdEncoding.DecodeString(cursor)
if err != nil {
return err
}
err = p.UnmarshalJSON(b)
if err != nil {
return err
}
return err
}
func parseCursor(in string) (p *PagingCursor, err error) {
if len(in) == 0 {
return nil, nil
@@ -130,7 +159,8 @@ func parseCursor(in string) (p *PagingCursor, err error) {
return nil, fmt.Errorf("could not decode cursor: %w", err)
}
if err = json.Unmarshal(buf, p); err != nil {
p = &PagingCursor{}
if err = p.UnmarshalJSON(buf); err != nil {
return nil, fmt.Errorf("could not decode cursor: %w", err)
}

View File

@@ -90,6 +90,61 @@ func TestUserListAll(t *testing.T) {
End()
}
func TestUserListWithPaging(t *testing.T) {
h := newHelper(t)
h.clearUsers()
h.secCtx()
seedCount := 40
for i := 0; i < seedCount; i++ {
h.createUserWithEmail(h.randEmail())
}
h.allow(types.UserRBACResource.AppendWildcard(), "read")
var aux = struct {
Response struct {
Filter struct {
NextPage *string
PrevPage *string
}
}
}{}
h.apiInit().
Get("/users/").
Query("limit", "13").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present(`$.response.filter`)).
Assert(jsonpath.Present(`$.response.set`)).
Assert(jsonpath.Len(`$.response.set`, 13)).
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
End().
JSON(&aux)
h.a.NotNil(aux.Response.Filter.NextPage)
h.apiInit().
Debug().
Get("/users/").
Query("limit", "13").
Query("pageCursor", *aux.Response.Filter.NextPage).
Expect(t).
Status(http.StatusOK).
Assert(jsonpath.Len(`$.response.set`, 13)).
Assert(jsonpath.Present(`$.response.filter.prevPage`)).
Assert(jsonpath.Present(`$.response.filter.nextPage`)).
End().
JSON(&aux)
h.a.NotNil(aux.Response.Filter.PrevPage)
h.a.NotNil(aux.Response.Filter.NextPage)
}
func TestUserList_filterForbidden(t *testing.T) {
h := newHelper(t)
h.clearUsers()