-
Notifications
You must be signed in to change notification settings - Fork 0
/
pages.go
146 lines (134 loc) · 4.05 KB
/
pages.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Package pagination provides support for pagination requests and responses.
package pagination
import (
"fmt"
"net/http"
"strconv"
"strings"
)
var (
// DefaultPageSize specifies the default page size
DefaultPageSize = 100
// MaxPageSize specifies the maximum page size
MaxPageSize = 1000
// PageVar specifies the query parameter name for page number
PageVar = "page"
// PageSizeVar specifies the query parameter name for page size
PageSizeVar = "per_page"
)
// Pages represents a paginated list of data items.
type Pages struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
PageCount int `json:"page_count"`
TotalCount int `json:"total_count"`
Items interface{} `json:"items"`
}
// New creates a new Pages instance.
// The page parameter is 1-based and refers to the current page index/number.
// The perPage parameter refers to the number of items on each page.
// And the total parameter specifies the total number of data items.
// If total is less than 0, it means total is unknown.
func New(page, perPage, total int) *Pages {
if perPage <= 0 {
perPage = DefaultPageSize
}
if perPage > MaxPageSize {
perPage = MaxPageSize
}
pageCount := -1
if total >= 0 {
pageCount = (total + perPage - 1) / perPage
if page > pageCount {
page = pageCount
}
}
if page < 1 {
page = 1
}
return &Pages{
Page: page,
PerPage: perPage,
TotalCount: total,
PageCount: pageCount,
}
}
// NewFromRequest creates a Pages object using the query parameters found in the given HTTP request.
// count stands for the total number of items. Use -1 if this is unknown.
func NewFromRequest(req *http.Request, count int) *Pages {
page := parseInt(req.URL.Query().Get(PageVar), 1)
perPage := parseInt(req.URL.Query().Get(PageSizeVar), DefaultPageSize)
return New(page, perPage, count)
}
// parseInt parses a string into an integer. If parsing is failed, defaultValue will be returned.
func parseInt(value string, defaultValue int) int {
if value == "" {
return defaultValue
}
if result, err := strconv.Atoi(value); err == nil {
return result
}
return defaultValue
}
// Offset returns the OFFSET value that can be used in a SQL statement.
func (p *Pages) Offset() int {
return (p.Page - 1) * p.PerPage
}
// Limit returns the LIMIT value that can be used in a SQL statement.
func (p *Pages) Limit() int {
return p.PerPage
}
// BuildLinkHeader returns an HTTP header containing the links about the pagination.
func (p *Pages) BuildLinkHeader(baseURL string, defaultPerPage int) string {
links := p.BuildLinks(baseURL, defaultPerPage)
header := ""
if links[0] != "" {
header += fmt.Sprintf("<%v>; rel=\"first\", ", links[0])
header += fmt.Sprintf("<%v>; rel=\"prev\"", links[1])
}
if links[2] != "" {
if header != "" {
header += ", "
}
header += fmt.Sprintf("<%v>; rel=\"next\"", links[2])
if links[3] != "" {
header += fmt.Sprintf(", <%v>; rel=\"last\"", links[3])
}
}
return header
}
// BuildLinks returns the first, prev, next, and last links corresponding to the pagination.
// A link could be an empty string if it is not needed.
// For example, if the pagination is at the first page, then both first and prev links
// will be empty.
func (p *Pages) BuildLinks(baseURL string, defaultPerPage int) [4]string {
var links [4]string
pageCount := p.PageCount
page := p.Page
if pageCount >= 0 && page > pageCount {
page = pageCount
}
if strings.Contains(baseURL, "?") {
baseURL += "&"
} else {
baseURL += "?"
}
if page > 1 {
links[0] = fmt.Sprintf("%v%v=%v", baseURL, PageVar, 1)
links[1] = fmt.Sprintf("%v%v=%v", baseURL, PageVar, page-1)
}
if pageCount >= 0 && page < pageCount {
links[2] = fmt.Sprintf("%v%v=%v", baseURL, PageVar, page+1)
links[3] = fmt.Sprintf("%v%v=%v", baseURL, PageVar, pageCount)
} else if pageCount < 0 {
links[2] = fmt.Sprintf("%v%v=%v", baseURL, PageVar, page+1)
}
if perPage := p.PerPage; perPage != defaultPerPage {
for i := 0; i < 4; i++ {
if links[i] != "" {
links[i] += fmt.Sprintf("&%v=%v", PageSizeVar, perPage)
}
}
}
return links
}