Files
descrybe/apps/api/internal/httpapi/pagination.go
T

109 lines
2.8 KiB
Go
Raw Normal View History

package httpapi
import (
"net/http"
"strconv"
"strings"
)
const (
defaultPageLimit = 50
maxPageLimit = 200
maxTreePageLimit = 2000
)
// QuerySearch returns the list/search text from query params.
// Accepts both `q` (canonical) and `search` (UI/legacy alias).
func QuerySearch(r *http.Request) string {
q := strings.TrimSpace(r.URL.Query().Get("q"))
if q != "" {
return q
}
return strings.TrimSpace(r.URL.Query().Get("search"))
}
// QueryTruthy reports whether a query param is an explicit truthy flag
// (1/true/yes/on). Empty or unrecognized values are false.
func QueryTruthy(r *http.Request, key string) bool {
v := strings.ToLower(strings.TrimSpace(r.URL.Query().Get(key)))
switch v {
case "1", "true", "yes", "on":
return true
default:
return false
}
}
// QueryDetailed is true when the client opts into full product list fields
// (JSONB attributes, descriptions, quality scoring inputs) via detailed=1.
func QueryDetailed(r *http.Request) bool {
return QueryTruthy(r, "detailed")
}
// ParseLimitOffset reads limit/offset query params with safe defaults and caps.
// Oversized limits are clamped to maxPageLimit.
func ParseLimitOffset(r *http.Request) (limit, offset int) {
return ParseLimitOffsetMax(r, maxPageLimit)
}
// ParseLimitOffsetMax allows a higher per-endpoint cap and clamps to max
// (used for category tree loads).
func ParseLimitOffsetMax(r *http.Request, max int) (limit, offset int) {
if max <= 0 {
max = maxPageLimit
}
limit, _ = strconv.Atoi(r.URL.Query().Get("limit"))
offset, _ = strconv.Atoi(r.URL.Query().Get("offset"))
if limit <= 0 {
limit = defaultPageLimit
}
if limit > max {
limit = max
}
if offset < 0 {
offset = 0
}
return limit, offset
}
// ParsePageLimitOffset supports legacy page/limit and v2 limit/offset.
// When page is set, offset = (page-1)*limit with legacy defaults (limit=25, max 100).
// When only offset/limit are set (no page), uses ParseLimitOffset defaults (limit=50, max 200).
func ParsePageLimitOffset(r *http.Request) (page, limit, offset int) {
pageRaw := strings.TrimSpace(r.URL.Query().Get("page"))
if pageRaw == "" {
limit, offset = ParseLimitOffset(r)
page = 1
if limit > 0 {
page = offset/limit + 1
}
return page, limit, offset
}
page, _ = strconv.Atoi(pageRaw)
if page < 1 {
page = 1
}
limit, _ = strconv.Atoi(r.URL.Query().Get("limit"))
if limit <= 0 {
limit = 25
}
if limit > 100 {
limit = 100
}
offset = (page - 1) * limit
return page, limit, offset
}
// pageSlice returns a bounded page of items and the original total length.
func pageSlice[T any](items []T, limit, offset int) (page []T, total int) {
total = len(items)
if offset >= total {
return []T{}, total
}
end := offset + limit
if end > total {
end = total
}
return items[offset:end], total
}