210 lines
4.1 KiB
Go
210 lines
4.1 KiB
Go
package influxdb
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
"github.com/influxdata/influxdb/v2/kit/platform/errors"
|
|
)
|
|
|
|
const (
|
|
DefaultPageSize = 20
|
|
MaxPageSize = 100
|
|
)
|
|
|
|
// PagingFilter represents a filter containing url query params.
|
|
type PagingFilter interface {
|
|
// QueryParams returns a map containing url query params.
|
|
QueryParams() map[string][]string
|
|
}
|
|
|
|
// PagingLinks represents paging links.
|
|
type PagingLinks struct {
|
|
Prev string `json:"prev,omitempty"`
|
|
Self string `json:"self"`
|
|
Next string `json:"next,omitempty"`
|
|
}
|
|
|
|
// FindOptions represents options passed to all find methods with multiple results.
|
|
type FindOptions struct {
|
|
Limit int
|
|
Offset int
|
|
After *platform.ID
|
|
SortBy string
|
|
Descending bool
|
|
}
|
|
|
|
// GetLimit returns the resolved limit between then limit boundaries.
|
|
// Given a limit <= 0 it returns the default limit.
|
|
func (f *FindOptions) GetLimit() int {
|
|
if f == nil || f.Limit <= 0 {
|
|
return DefaultPageSize
|
|
}
|
|
|
|
if f.Limit > MaxPageSize {
|
|
return MaxPageSize
|
|
}
|
|
|
|
return f.Limit
|
|
}
|
|
|
|
// DecodeFindOptions returns a FindOptions decoded from http request.
|
|
func DecodeFindOptions(r *http.Request) (*FindOptions, error) {
|
|
opts := &FindOptions{}
|
|
qp := r.URL.Query()
|
|
|
|
if offset := qp.Get("offset"); offset != "" {
|
|
o, err := strconv.Atoi(offset)
|
|
if err != nil {
|
|
return nil, &errors.Error{
|
|
Code: errors.EInvalid,
|
|
Msg: "offset is invalid",
|
|
}
|
|
}
|
|
|
|
opts.Offset = o
|
|
}
|
|
|
|
if after := qp.Get("after"); after != "" {
|
|
id, err := platform.IDFromString(after)
|
|
if err != nil {
|
|
return nil, &errors.Error{
|
|
Code: errors.EInvalid,
|
|
Err: fmt.Errorf("decoding after: %w", err),
|
|
}
|
|
}
|
|
|
|
opts.After = id
|
|
}
|
|
|
|
if limit := qp.Get("limit"); limit != "" {
|
|
l, err := strconv.Atoi(limit)
|
|
if err != nil {
|
|
return nil, &errors.Error{
|
|
Code: errors.EInvalid,
|
|
Msg: "limit is invalid",
|
|
}
|
|
}
|
|
|
|
if l < 1 || l > MaxPageSize {
|
|
return nil, &errors.Error{
|
|
Code: errors.EInvalid,
|
|
Msg: fmt.Sprintf("limit must be between 1 and %d", MaxPageSize),
|
|
}
|
|
}
|
|
|
|
opts.Limit = l
|
|
} else {
|
|
opts.Limit = DefaultPageSize
|
|
}
|
|
|
|
if sortBy := qp.Get("sortBy"); sortBy != "" {
|
|
opts.SortBy = sortBy
|
|
}
|
|
|
|
if descending := qp.Get("descending"); descending != "" {
|
|
desc, err := strconv.ParseBool(descending)
|
|
if err != nil {
|
|
return nil, &errors.Error{
|
|
Code: errors.EInvalid,
|
|
Msg: "descending is invalid",
|
|
}
|
|
}
|
|
|
|
opts.Descending = desc
|
|
}
|
|
|
|
return opts, nil
|
|
}
|
|
|
|
func FindOptionParams(opts ...FindOptions) [][2]string {
|
|
var out [][2]string
|
|
for _, o := range opts {
|
|
for k, vals := range o.QueryParams() {
|
|
for _, v := range vals {
|
|
out = append(out, [2]string{k, v})
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// QueryParams returns a map containing url query params.
|
|
func (f FindOptions) QueryParams() map[string][]string {
|
|
qp := map[string][]string{
|
|
"descending": {strconv.FormatBool(f.Descending)},
|
|
"offset": {strconv.Itoa(f.Offset)},
|
|
}
|
|
|
|
if f.After != nil {
|
|
qp["after"] = []string{f.After.String()}
|
|
}
|
|
|
|
if f.Limit > 0 {
|
|
qp["limit"] = []string{strconv.Itoa(f.Limit)}
|
|
}
|
|
|
|
if f.SortBy != "" {
|
|
qp["sortBy"] = []string{f.SortBy}
|
|
}
|
|
|
|
return qp
|
|
}
|
|
|
|
// NewPagingLinks returns a PagingLinks.
|
|
// num is the number of returned results.
|
|
func NewPagingLinks(basePath string, opts FindOptions, f PagingFilter, num int) *PagingLinks {
|
|
u := url.URL{
|
|
Path: basePath,
|
|
}
|
|
|
|
values := url.Values{}
|
|
for k, vs := range f.QueryParams() {
|
|
for _, v := range vs {
|
|
if v != "" {
|
|
values.Add(k, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
var self, next, prev string
|
|
for k, vs := range opts.QueryParams() {
|
|
for _, v := range vs {
|
|
if v != "" {
|
|
values.Add(k, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
u.RawQuery = values.Encode()
|
|
self = u.String()
|
|
|
|
if num >= opts.Limit {
|
|
nextOffset := opts.Offset + opts.Limit
|
|
values.Set("offset", strconv.Itoa(nextOffset))
|
|
u.RawQuery = values.Encode()
|
|
next = u.String()
|
|
}
|
|
|
|
if opts.Offset > 0 {
|
|
prevOffset := opts.Offset - opts.Limit
|
|
if prevOffset < 0 {
|
|
prevOffset = 0
|
|
}
|
|
values.Set("offset", strconv.Itoa(prevOffset))
|
|
u.RawQuery = values.Encode()
|
|
prev = u.String()
|
|
}
|
|
|
|
links := &PagingLinks{
|
|
Prev: prev,
|
|
Self: self,
|
|
Next: next,
|
|
}
|
|
|
|
return links
|
|
}
|