Skip to content

Commit

Permalink
feat: add token pagination link header parser (#722)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr authored Sep 6, 2023
1 parent d1b24d4 commit fbf9d35
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ require (
github.com/ory/herodot v0.9.13
github.com/ory/jsonschema/v3 v3.0.7
github.com/pelletier/go-toml v1.9.5
github.com/peterhellberg/link v1.2.0
github.com/pkg/errors v0.9.1
github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.13.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
Expand Down
44 changes: 44 additions & 0 deletions pagination/keysetpagination/parse_header.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package keysetpagination

import (
"net/http"
"net/url"

"github.com/peterhellberg/link"
)

// PaginationResult represents a parsed result of the link HTTP header.
type PaginationResult struct {
// NextToken is the next page token. If it's empty, there is no next page.
NextToken string

// FirstToken is the first page token.
FirstToken string
}

// ParseHeader parses the response header's Link.
func ParseHeader(r *http.Response) *PaginationResult {
links := link.ParseResponse(r)
return &PaginationResult{
NextToken: findRel(links, "next"),
FirstToken: findRel(links, "first"),
}
}

func findRel(links link.Group, rel string) string {
for idx, l := range links {
if idx == rel {
parsed, err := url.Parse(l.URI)
if err != nil {
continue
}

return parsed.Query().Get("page_token")
}
}

return ""
}
49 changes: 49 additions & 0 deletions pagination/keysetpagination/parse_header_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package keysetpagination

import (
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestParseHeader(t *testing.T) {
u, err := url.Parse("https://www.ory.sh/")
require.NoError(t, err)

t.Run("has next page", func(t *testing.T) {
p := &Paginator{
defaultToken: StringPageToken("default"),
token: StringPageToken("next"),
size: 2,
}

r := httptest.NewRecorder()
Header(r, u, p)

result := ParseHeader(&http.Response{Header: r.Header()})
assert.Equal(t, "next", result.NextToken, r.Header())
assert.Equal(t, "default", result.FirstToken, r.Header())
})

t.Run("is last page", func(t *testing.T) {
p := &Paginator{
defaultToken: StringPageToken("default"),
size: 1,
isLast: true,
}

r := httptest.NewRecorder()
Header(r, u, p)

result := ParseHeader(&http.Response{Header: r.Header()})
assert.Equal(t, "", result.NextToken, r.Header())
assert.Equal(t, "default", result.FirstToken, r.Header())
})
}

0 comments on commit fbf9d35

Please sign in to comment.