kratos/x/pagination.go

66 lines
2.2 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package x
import (
"net/http"
"net/url"
"github.com/ory/herodot"
"github.com/ory/x/pagination/keysetpagination"
"github.com/ory/x/pagination/migrationpagination"
"github.com/ory/x/pagination/pagepagination"
)
// The format we need to use in the Page tokens, as it's the only format that is understood by all DBs
const MapPaginationDateFormat = "2006-01-02 15:04:05.99999"
// ParsePagination parses limit and page from *http.Request with given limits and defaults.
func ParsePagination(r *http.Request) (page, itemsPerPage int) {
return migrationpagination.NewDefaultPaginator().ParsePagination(r)
}
func PaginationHeader(w http.ResponseWriter, u url.URL, total int64, page, itemsPerPage int) {
migrationpagination.PaginationHeader(w, &u, total, page, itemsPerPage)
}
type Page struct {
Page, ItemsPerPage int
}
var PagePaginationLimit = 1000
func ParseKeysetOrPagePagination(r *http.Request) ([]keysetpagination.Option, *Page, error) {
q := r.URL.Query()
// If we have any new-style pagination parameters, use those and ignore the rest.
if q.Has("page_token") || q.Has("page_size") {
keyset, err := keysetpagination.Parse(q, keysetpagination.NewStringPageToken)
if err != nil {
return nil, nil, herodot.ErrBadRequest.WithReason(err.Error())
}
return keyset, nil, nil
}
// allow fallback page pagination with upper limit
if q.Has("page") {
paginator := pagepagination.PagePaginator{MaxItems: 500, DefaultItems: 250}
page, perPage := paginator.ParsePagination(r)
if page*perPage > PagePaginationLimit {
return nil, nil, herodot.ErrBadRequest.WithReasonf("Legacy pagination is not supported for enumerating over %d items. Please switch to using page_token and page_size.", PagePaginationLimit)
}
return nil, &Page{page, perPage}, nil
}
// Allow passing per_page instead of page_size if only the former is set...
if q.Has("per_page") && !q.Has("page_size") {
q.Set("page_size", q.Get("per_page"))
q.Del("per_page")
r.URL.RawQuery = q.Encode()
}
// ... and defaul to keyset pagination
keyset, err := keysetpagination.Parse(q, keysetpagination.NewStringPageToken)
if err != nil {
return nil, nil, herodot.ErrBadRequest.WithReason(err.Error())
}
return keyset, nil, nil
}