3
0

Add support for limit/filter

This commit is contained in:
Denis Arh
2020-03-27 10:36:10 +01:00
parent 5a966a2303
commit 7fec663f2c
13 changed files with 169 additions and 40 deletions
+1 -1
View File
@@ -153,7 +153,7 @@ func (r attachment) Find(filter types.AttachmentFilter) (set types.AttachmentSet
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r attachment) Create(mod *types.Attachment) (*types.Attachment, error) {
+1 -1
View File
@@ -133,7 +133,7 @@ func (r chart) Find(filter types.ChartFilter) (set types.ChartSet, f types.Chart
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r chart) Create(mod *types.Chart) (*types.Chart, error) {
+1 -1
View File
@@ -154,7 +154,7 @@ func (r module) Find(filter types.ModuleFilter) (set types.ModuleSet, f types.Mo
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r module) Create(mod *types.Module) (*types.Module, error) {
+1 -1
View File
@@ -131,7 +131,7 @@ func (r *namespace) Find(filter types.NamespaceFilter) (set types.NamespaceSet,
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r *namespace) Create(mod *types.Namespace) (*types.Namespace, error) {
+1 -1
View File
@@ -153,7 +153,7 @@ func (r page) Find(filter types.PageFilter) (set types.PageSet, f types.PageFilt
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r page) Reorder(namespaceID, parentID uint64, pageIDs []uint64) error {
+1 -1
View File
@@ -137,7 +137,7 @@ func (r record) Find(module *types.Module, filter types.RecordFilter) (set types
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
// Export ignores paging and does not return filter
+69 -22
View File
@@ -9,15 +9,24 @@ const (
)
type (
// @todo this needs to be refactored to support
// limit/offset params alongside page/perPage
// PageFilter supports page/perPage (one based) and limit/offset
// pagination.
//
// Limit/offset is prioritised over page/perPage
//
PageFilter struct {
Page uint `json:"page"`
PerPage uint `json:"perPage"`
Count uint `json:"count"`
// If limit is set to a positive number,
// paging mechanisms will use limit/offset
// Otherwise page/perPage is used
Limit uint `json:"limit,omitempty"`
Offset uint `json:"offset,omitempty"`
// Limit uint `json:"limit"`
// Offset uint `json:"offset"`
Page uint `json:"page,omitempty"`
PerPage uint `json:"perPage,omitempty"`
// Count is used when filter and pagination are send back
// with the response
Count uint `json:"count"`
}
)
@@ -32,24 +41,62 @@ func Paging(page, perPage uint) PageFilter {
}
}
func (pf *PageFilter) ParsePagination(input interface{}) {
// Limit creates PageFilter struct from limit and, optionally offset
func Limit(a ...uint) PageFilter {
switch len(a) {
case 1:
return PageFilter{Limit: a[0]}
case 2:
return PageFilter{Limit: a[0], Offset: a[1]}
}
return PageFilter{}
}
func (pf *PageFilter) ParsePagination(input interface{}) error {
return parsePagination(pf, input)
}
func parsePagination(pf *PageFilter, input interface{}) (err error) {
switch i := input.(type) {
case map[string]string:
if len(i["limit"]+i["offset"]) > 0 {
// @todo to properly & fully support limit & offset
// we need to refactor pagination handling
limit, _ := strconv.ParseUint(i["limit"], 10, 32)
//offset, _ := strconv.ParseUint(i["offset"], 10, 32)
conv := func(v *uint, name string) error {
if _, has := i[name]; has {
pv, err := strconv.ParseUint(i[name], 10, 32)
if err != nil {
return err
}
// only basic support for now due to
// limitation of PageFilter implementation
pf.PerPage = uint(limit)
//pf.Page = offset / limit
} else if len(i["page"]+i["perPage"]) > 0 {
page, _ := strconv.ParseUint(i["page"], 10, 32)
perPage, _ := strconv.ParseUint(i["perPage"], 10, 32)
pf.Page = uint(page)
pf.PerPage = uint(perPage)
*v = uint(pv)
}
return nil
}
if len(i["limit"]+i["offset"]) > 0 {
if err = conv(&pf.Limit, "limit"); err != nil {
return
}
if err = conv(&pf.Offset, "offset"); err != nil {
return
}
return
}
if len(i["page"]+i["perPage"]) > 0 {
if err = conv(&pf.Page, "page"); err != nil {
return
}
if err = conv(&pf.PerPage, "perPage"); err != nil {
return
}
return
}
}
return nil
}
+73
View File
@@ -0,0 +1,73 @@
package rh
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
)
func TestLimit(t *testing.T) {
var (
r = require.New(t)
)
r.Equal(Limit(42).Limit, uint(42))
r.Equal(Limit(0, 42).Offset, uint(42))
}
func Test_parsePagination(t *testing.T) {
var (
tests = []struct {
name string
args interface{}
pf PageFilter
wantErr bool
}{
{
"empty",
nil,
PageFilter{},
false,
},
{
"valid l/o",
map[string]string{"limit": "42", "offset": "314"},
PageFilter{Limit: 42, Offset: 314},
false,
},
{
"mixed",
map[string]string{"page": "42", "limit": "314"},
PageFilter{Limit: 314, Offset: 0},
false,
},
{
"invalid limit",
map[string]string{"limit": "abc"},
PageFilter{},
true,
},
{
"invalid page",
map[string]string{"page": "abc"},
PageFilter{},
true,
},
}
)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var (
pf = PageFilter{}
)
if err := parsePagination(&pf, tt.args); (err != nil) != tt.wantErr {
t.Errorf("parsePagination() error = %v, wantErr %v", err, tt.wantErr)
} else if !reflect.DeepEqual(pf, tt.pf) {
t.Errorf("\n actual: %v\nexpected: %v\n", pf, tt.pf)
}
})
}
}
+17 -8
View File
@@ -45,18 +45,27 @@ func Count(db *factory.DB, q squirrel.SelectBuilder) (count uint, err error) {
}
// FetchPaged fetches paged rows
func FetchPaged(db *factory.DB, q squirrel.SelectBuilder, page, perPage uint, set interface{}) error {
if perPage > 0 {
q = q.Limit(uint64(perPage))
func FetchPaged(db *factory.DB, q squirrel.SelectBuilder, p PageFilter, set interface{}) error {
if p.Limit+p.Offset == 0 {
// When both, offset & limit are 0,
// calculate both values from page/perPage params
if p.PerPage > 0 {
p.Limit = p.PerPage
}
if p.Page < 1 {
p.Page = 1
}
p.Offset = uint((p.Page - 1) * p.PerPage)
}
if page < 1 {
page = 1
if p.Limit > 0 {
q = q.Limit(uint64(p.Limit))
}
var offset = uint64((page - 1) * perPage)
if offset > 0 {
q = q.Offset(offset)
if p.Offset > 0 {
q = q.Offset(uint64(p.Limit))
}
return FetchAll(db, q, set)
+1 -1
View File
@@ -126,7 +126,7 @@ func (r *application) Find(filter types.ApplicationFilter) (set types.Applicatio
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r *application) Create(mod *types.Application) (*types.Application, error) {
+1 -1
View File
@@ -147,7 +147,7 @@ func (r reminder) Find(filter types.ReminderFilter) (set types.ReminderSet, f ty
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r reminder) Create(mod *types.Reminder) (rm *types.Reminder, err error) {
+1 -1
View File
@@ -170,7 +170,7 @@ func (r *role) Find(filter types.RoleFilter) (set types.RoleSet, f types.RoleFil
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r *role) Create(mod *types.Role) (*types.Role, error) {
+1 -1
View File
@@ -207,7 +207,7 @@ func (r user) Find(filter types.UserFilter) (set types.UserSet, f types.UserFilt
return
}
return set, f, rh.FetchPaged(r.db(), query, f.Page, f.PerPage, &set)
return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
}
func (r user) Total() (count uint) {