-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add package pagination * Fix linter error * Rename pagination options struct
- Loading branch information
1 parent
e5c00c4
commit 952a4a1
Showing
2 changed files
with
199 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}) | ||
} | ||
} |