[bugfix] self-referencing collection pages for status replies (#2364)

This commit is contained in:
kim
2023-11-20 12:22:28 +00:00
committed by GitHub
parent efefdb1323
commit 16275853eb
24 changed files with 611 additions and 427 deletions

View File

@@ -19,9 +19,8 @@ package paging
import (
"net/url"
"slices"
"strconv"
"golang.org/x/exp/slices"
)
type Page struct {
@@ -117,7 +116,7 @@ func (p *Page) Page(in []string) []string {
// Output slice must
// ALWAYS be descending.
in = Reverse(in)
slices.Reverse(in)
}
} else {
// Default sort is descending,
@@ -143,6 +142,66 @@ func (p *Page) Page(in []string) []string {
return in
}
// Page_PageFunc is functionally equivalent to Page{}.Page(), but for an arbitrary type with ID.
// Note: this is not a Page{} method as Go generics are not supported in method receiver functions.
func Page_PageFunc[WithID any](p *Page, in []WithID, get func(WithID) string) []WithID { //nolint:revive
if p == nil {
// no paging.
return in
}
if p.order().Ascending() {
// Sort type is ascending, input
// data is assumed to be ascending.
if minIdx := Boundary_FindFunc(p.Min, in, get); minIdx != -1 {
// Reslice skipping up to min.
in = in[minIdx+1:]
}
if maxIdx := Boundary_FindFunc(p.Max, in, get); maxIdx != -1 {
// Reslice stripping past max.
in = in[:maxIdx]
}
if p.Limit > 0 && p.Limit < len(in) {
// Reslice input to limit.
in = in[:p.Limit]
}
if len(in) > 1 {
// Clone input before
// any modifications.
in = slices.Clone(in)
// Output slice must
// ALWAYS be descending.
slices.Reverse(in)
}
} else {
// Default sort is descending,
// catching all cases when NOT
// ascending (even zero value).
if maxIdx := Boundary_FindFunc(p.Max, in, get); maxIdx != -1 {
// Reslice skipping up to max.
in = in[maxIdx+1:]
}
if minIdx := Boundary_FindFunc(p.Min, in, get); minIdx != -1 {
// Reslice stripping past min.
in = in[:minIdx]
}
if p.Limit > 0 && p.Limit < len(in) {
// Reslice input to limit.
in = in[:p.Limit]
}
}
return in
}
// Next creates a new instance for the next returnable page, using
// given max value. This preserves original limit and max key name.
func (p *Page) Next(lo, hi string) *Page {
@@ -225,21 +284,24 @@ func (p *Page) ToLinkURL(proto, host, path string, queryParams url.Values) *url.
if queryParams == nil {
// Allocate new query parameters.
queryParams = make(url.Values)
} else {
// Before edit clone existing params.
queryParams = cloneQuery(queryParams)
}
if p.Min.Value != "" {
// A page-minimum query parameter is available.
queryParams.Add(p.Min.Name, p.Min.Value)
queryParams.Set(p.Min.Name, p.Min.Value)
}
if p.Max.Value != "" {
// A page-maximum query parameter is available.
queryParams.Add(p.Max.Name, p.Max.Value)
queryParams.Set(p.Max.Name, p.Max.Value)
}
if p.Limit > 0 {
// A page limit query parameter is available.
queryParams.Add("limit", strconv.Itoa(p.Limit))
queryParams.Set("limit", strconv.Itoa(p.Limit))
}
// Build URL string.
@@ -250,3 +312,12 @@ func (p *Page) ToLinkURL(proto, host, path string, queryParams url.Values) *url.
RawQuery: queryParams.Encode(),
}
}
// cloneQuery clones input map of url values.
func cloneQuery(src url.Values) url.Values {
dst := make(url.Values, len(src))
for k, vs := range src {
dst[k] = slices.Clone(vs)
}
return dst
}