Skip to content

Commit

Permalink
Add package pagination (#94)
Browse files Browse the repository at this point in the history
* Add package pagination

* Fix linter error

* Rename pagination options struct
  • Loading branch information
krithika369 authored May 14, 2024
1 parent e5c00c4 commit 952a4a1
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
68 changes: 68 additions & 0 deletions api/pkg/pagination/pagination.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package pagination

import (
"fmt"
"math"
)

// Paging can be used to capture paging information in API responses.
type Paging struct {
Page int32
Pages int32
Total int32
}

// Options can be used to supply pagination filter options to APIs.
type Options struct {
Page *int32 `json:"page,omitempty"`
PageSize *int32 `json:"page_size,omitempty"`
}

// Paginator handles common pagination workflows.
type Paginator struct {
DefaultPage int32
DefaultPageSize int32
MaxPageSize int32
}

func NewPaginator(defaultPage int32, defaultPageSize int32, maxPageSize int32) Paginator {
return Paginator{
DefaultPage: defaultPage,
DefaultPageSize: defaultPageSize,
MaxPageSize: maxPageSize,
}
}

func (p Paginator) NewPaginationOptions(page *int32, pageSize *int32) Options {
if page == nil {
page = &p.DefaultPage
}
if pageSize == nil {
pageSize = &p.DefaultPageSize
}

return Options{
Page: page,
PageSize: pageSize,
}
}

func (p Paginator) ValidatePaginationParams(page *int32, pageSize *int32) error {
if pageSize != nil && (*pageSize <= 0 || *pageSize > p.MaxPageSize) {
return fmt.Errorf("page size must be within range (0 < page_size <= %d) or unset",
p.MaxPageSize)
}
if page != nil && *page <= 0 {
return fmt.Errorf("page must be > 0 or unset")
}

return nil
}

func ToPaging(opts Options, count int) *Paging {
return &Paging{
Page: *opts.Page,
Pages: int32(math.Ceil(float64(count) / float64(*opts.PageSize))),
Total: int32(count),
}
}
131 changes: 131 additions & 0 deletions api/pkg/pagination/pagination_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package pagination

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewPaginationOptions(t *testing.T) {
one, two, three, ten := int32(1), int32(2), int32(3), int32(10)
paginator := NewPaginator(1, 10, 50)

tests := map[string]struct {
page *int32
pageSize *int32
expected Options
}{
"defaults": {
expected: Options{
Page: &one,
PageSize: &ten,
},
},
"missing page": {
pageSize: &three,
expected: Options{
Page: &one,
PageSize: &three,
},
},
"missing page size": {
page: &two,
expected: Options{
Page: &two,
PageSize: &ten,
},
},
"new values": {
page: &two,
pageSize: &three,
expected: Options{
Page: &two,
PageSize: &three,
},
},
}

for name, data := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(t, data.expected, paginator.NewPaginationOptions(data.page, data.pageSize))
})
}
}

func TestValidatePaginationParams(t *testing.T) {
zero, one, two, hundred := int32(0), int32(1), int32(2), int32(100)
paginator := NewPaginator(1, 10, 50)

tests := map[string]struct {
page *int32
pageSize *int32
expectedError string
}{
"zero page size": {
pageSize: &zero,
expectedError: "page size must be within range (0 < page_size <= 50) or unset",
},
"large page size": {
pageSize: &hundred,
expectedError: "page size must be within range (0 < page_size <= 50) or unset",
},
"zero page": {
page: &zero,
expectedError: "page must be > 0 or unset",
},
"success - all values unset": {},
"success - all values set": {
page: &one,
pageSize: &two,
},
}

for name, data := range tests {
t.Run(name, func(t *testing.T) {
err := paginator.ValidatePaginationParams(data.page, data.pageSize)
if data.expectedError == "" {
assert.Nil(t, err)
} else {
assert.EqualError(t, err, data.expectedError)
}
})
}
}

func TestPaging(t *testing.T) {
tests := []struct {
page int32
pageSize int32
count int
expected Paging
}{
{
page: int32(1),
pageSize: int32(10),
count: 5,
expected: Paging{
Page: 1,
Pages: 1,
Total: 5,
},
},
{
page: int32(2),
pageSize: int32(3),
count: 7,
expected: Paging{
Page: 2,
Pages: 3,
Total: 7,
},
},
}

for idx, data := range tests {
t.Run(fmt.Sprintf("case %d", idx), func(t *testing.T) {
actual := ToPaging(Options{Page: &data.page, PageSize: &data.pageSize}, data.count)
assert.Equal(t, data.expected, *actual)
})
}
}

0 comments on commit 952a4a1

Please sign in to comment.