Shuffle around source organization.
Utility -> Pages -> Pagers
diff --git a/pagination.go b/pagination.go
index d6fa5c4..85f3286 100644
--- a/pagination.go
+++ b/pagination.go
@@ -15,6 +15,43 @@
ErrPageNotAvailable = errors.New("The requested Collection page does not exist.")
)
+// LastHTTPResponse stores generic information derived from an HTTP response.
+type LastHTTPResponse struct {
+ http.Header
+ Body map[string]interface{}
+}
+
+// RememberHTTPResponse parses an HTTP response as JSON and returns a LastHTTPResponse containing the results.
+// The main reason to do this instead of holding the response directly is that a response body can only be read once.
+// Also, this centralizes the JSON decoding.
+func RememberHTTPResponse(resp http.Response) (LastHTTPResponse, error) {
+ var parsedBody map[string]interface{}
+
+ defer resp.Body.Close()
+ rawBody, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return LastHTTPResponse{}, err
+ }
+ err = json.Unmarshal(rawBody, &parsedBody)
+ if err != nil {
+ return LastHTTPResponse{}, err
+ }
+
+ return LastHTTPResponse{Header: resp.Header, Body: parsedBody}, err
+}
+
+// request performs a Perigee request and extracts the http.Response from the result.
+func request(client *ServiceClient, url string) (http.Response, error) {
+ resp, err := perigee.Request("GET", url, perigee.Options{
+ MoreHeaders: client.Provider.AuthenticatedHeaders(),
+ OkCodes: []int{200},
+ })
+ if err != nil {
+ return http.Response{}, err
+ }
+ return resp.HttpResponse, nil
+}
+
// Page must be satisfied by the result type of any resource collection.
// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated.
type Page interface {
@@ -24,6 +61,38 @@
NextPageURL() (string, error)
}
+// SinglePage is a page that contains all of the results from an operation.
+type SinglePage LastHTTPResponse
+
+// NextPageURL always returns "" to indicate that there are no more pages to return.
+func (current SinglePage) NextPageURL() (string, error) {
+ return "", nil
+}
+
+// LinkedPage is a page in a collection that provides navigational "Next" and "Previous" links within its result.
+type LinkedPage LastHTTPResponse
+
+// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present.
+func (current LinkedPage) NextPageURL() (string, error) {
+ type response struct {
+ Links struct {
+ Next *string `mapstructure:"next,omitempty"`
+ } `mapstructure:"links"`
+ }
+
+ var r response
+ err := mapstructure.Decode(current.Body, &r)
+ if err != nil {
+ return "", err
+ }
+
+ if r.Links.Next == nil {
+ return "", nil
+ }
+
+ return *r.Links.Next, nil
+}
+
// Pager knows how to advance through a specific resource collection, one page at a time.
type Pager struct {
initialURL string
@@ -40,6 +109,55 @@
}
}
+// NewSinglePager constructs a Pager that "iterates" over a single Page.
+// Supply the URL to request.
+func NewSinglePager(client *ServiceClient, onlyURL string) Pager {
+ consumed := false
+ single := func(_ string) (Page, error) {
+ if !consumed {
+ consumed = true
+ resp, err := request(client, onlyURL)
+ if err != nil {
+ return SinglePage{}, err
+ }
+
+ cp, err := RememberHTTPResponse(resp)
+ if err != nil {
+ return SinglePage{}, err
+ }
+ return SinglePage(cp), nil
+ }
+ return SinglePage{}, ErrPageNotAvailable
+ }
+
+ return Pager{
+ initialURL: "",
+ fetchNextPage: single,
+ }
+}
+
+// NewLinkedPager creates a Pager that uses a "links" element in the JSON response to locate the next page.
+func NewLinkedPager(client *ServiceClient, initialURL string) Pager {
+ fetchNextPage := func(url string) (Page, error) {
+ resp, err := request(client, url)
+ if err != nil {
+ return nil, err
+ }
+
+ cp, err := RememberHTTPResponse(resp)
+ if err != nil {
+ return nil, err
+ }
+
+ return LinkedPage(cp), nil
+ }
+
+ return Pager{
+ initialURL: initialURL,
+ fetchNextPage: fetchNextPage,
+ }
+}
+
// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function.
// Return "false" from the handler to prematurely stop iterating.
func (p Pager) EachPage(handler func(Page) (bool, error)) error {
@@ -67,118 +185,3 @@
}
}
}
-
-// LastHTTPResponse stores generic information derived from an HTTP response.
-type LastHTTPResponse struct {
- http.Header
- Body map[string]interface{}
-}
-
-// RememberHTTPResponse parses an HTTP response as JSON and returns a LastHTTPResponse containing the results.
-func RememberHTTPResponse(resp http.Response) (LastHTTPResponse, error) {
- var parsedBody map[string]interface{}
-
- defer resp.Body.Close()
- rawBody, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return LastHTTPResponse{}, err
- }
- err = json.Unmarshal(rawBody, &parsedBody)
- if err != nil {
- return LastHTTPResponse{}, err
- }
-
- return LastHTTPResponse{Header: resp.Header, Body: parsedBody}, err
-}
-
-func request(client *ServiceClient, url string) (http.Response, error) {
- resp, err := perigee.Request("GET", url, perigee.Options{
- MoreHeaders: client.Provider.AuthenticatedHeaders(),
- OkCodes: []int{200},
- })
- if err != nil {
- return http.Response{}, err
- }
- return resp.HttpResponse, nil
-}
-
-// SinglePage is a page that contains all of the results from an operation.
-type SinglePage LastHTTPResponse
-
-// NextPageURL always returns "" to indicate that there are no more pages to return.
-func (current SinglePage) NextPageURL() (string, error) {
- return "", nil
-}
-
-// NewSinglePager constructs a Pager that "iterates" over a single Page.
-// Supply the URL to request.
-func NewSinglePager(client *ServiceClient, onlyURL string) Pager {
- consumed := false
- single := func(_ string) (Page, error) {
- if !consumed {
- consumed = true
- resp, err := request(client, onlyURL)
- if err != nil {
- return SinglePage{}, err
- }
-
- cp, err := RememberHTTPResponse(resp)
- if err != nil {
- return SinglePage{}, err
- }
- return SinglePage(cp), nil
- }
- return SinglePage{}, ErrPageNotAvailable
- }
-
- return Pager{
- initialURL: "",
- fetchNextPage: single,
- }
-}
-
-// LinkedPage is a page in a collection that provides navigational "Next" and "Previous" links within its result.
-type LinkedPage LastHTTPResponse
-
-// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present.
-func (current LinkedPage) NextPageURL() (string, error) {
- type response struct {
- Links struct {
- Next *string `mapstructure:"next,omitempty"`
- } `mapstructure:"links"`
- }
-
- var r response
- err := mapstructure.Decode(current.Body, &r)
- if err != nil {
- return "", err
- }
-
- if r.Links.Next == nil {
- return "", nil
- }
-
- return *r.Links.Next, nil
-}
-
-// NewLinkedPager creates a Pager that uses a "links" element in the JSON response to locate the next page.
-func NewLinkedPager(client *ServiceClient, initialURL string) Pager {
- fetchNextPage := func(url string) (Page, error) {
- resp, err := request(client, url)
- if err != nil {
- return nil, err
- }
-
- cp, err := RememberHTTPResponse(resp)
- if err != nil {
- return nil, err
- }
-
- return LinkedPage(cp), nil
- }
-
- return Pager{
- initialURL: initialURL,
- fetchNextPage: fetchNextPage,
- }
-}