all page marker/single/linked ops and unit tests
diff --git a/pagination/linked.go b/pagination/linked.go
index 461fa49..e9bd8de 100644
--- a/pagination/linked.go
+++ b/pagination/linked.go
@@ -59,3 +59,9 @@
}
}
}
+
+// GetBody returns the linked page's body. This method is needed to satisfy the
+// Page interface.
+func (current LinkedPageBase) GetBody() interface{} {
+ return current.Body
+}
diff --git a/pagination/linked_test.go b/pagination/linked_test.go
index 4d3248e..e2ba7e5 100644
--- a/pagination/linked_test.go
+++ b/pagination/linked_test.go
@@ -105,3 +105,17 @@
t.Errorf("Expected 3 calls, but was %d", callCount)
}
}
+
+func TestAllPagesLinked(t *testing.T) {
+ pager := createLinked(t)
+ defer testhelper.TeardownHTTP()
+
+ pager.PageType = LinkedPageResult{}
+ page, err := pager.AllPages()
+ testhelper.AssertNoErr(t, err)
+
+ expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
+ actual, err := ExtractLinkedInts(page)
+ testhelper.AssertNoErr(t, err)
+ testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/pagination/marker.go b/pagination/marker.go
index e7688c2..f355afc 100644
--- a/pagination/marker.go
+++ b/pagination/marker.go
@@ -32,3 +32,9 @@
return currentURL.String(), nil
}
+
+// GetBody returns the linked page's body. This method is needed to satisfy the
+// Page interface.
+func (current MarkerPageBase) GetBody() interface{} {
+ return current.Body
+}
diff --git a/pagination/marker_test.go b/pagination/marker_test.go
index 3b1df1d..f003177 100644
--- a/pagination/marker_test.go
+++ b/pagination/marker_test.go
@@ -111,3 +111,17 @@
testhelper.AssertNoErr(t, err)
testhelper.AssertEquals(t, callCount, 3)
}
+
+func TestAllPagesMarker(t *testing.T) {
+ pager := createMarkerPaged(t)
+ defer testhelper.TeardownHTTP()
+
+ pager.PageType = MarkerPageResult{}
+ page, err := pager.AllPages()
+ testhelper.AssertNoErr(t, err)
+
+ expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"}
+ actual, err := ExtractMarkerStrings(page)
+ testhelper.AssertNoErr(t, err)
+ testhelper.CheckDeepEquals(t, expected, actual)
+}
diff --git a/pagination/pager.go b/pagination/pager.go
index 5c20e16..8e55837 100644
--- a/pagination/pager.go
+++ b/pagination/pager.go
@@ -2,6 +2,8 @@
import (
"errors"
+ "fmt"
+ "reflect"
"github.com/rackspace/gophercloud"
)
@@ -25,6 +27,9 @@
// IsEmpty returns true if this Page has no items in it.
IsEmpty() (bool, error)
+
+ // GetBody returns the Page Body. This is used in the `AllPages`.
+ GetBody() interface{}
}
// Pager knows how to advance through a specific resource collection, one page at a time.
@@ -39,6 +44,8 @@
// Headers supplies additional HTTP headers to populate on each paged request.
Headers map[string]string
+
+ PageType Page
}
// NewPager constructs a manually-configured pager.
@@ -113,3 +120,84 @@
}
}
}
+
+// AllPages returns all the pages from a `List` operation in a single page,
+// allowing the user to retrieve all the pages at once.
+func (p Pager) AllPages() (Page, error) {
+ // pagesSlice holds all the pages until they get converted into as Page Body.
+ var pagesSlice []interface{}
+ // body will contain the final concatenated Page body.
+ var body reflect.Value
+
+ // Grab a test page to ascertain the page body type.
+ testPage, err := p.fetchNextPage(p.initialURL)
+ if err != nil {
+ return nil, err
+ }
+
+ // Switch on the page body type. Recognized types are `map[string]interface{}`
+ // and `[]byte`.
+ switch testPage.GetBody().(type) {
+ case map[string]interface{}:
+ // key is the map key for the page body if the body type is `map[string]interface{}`.
+ var key string
+ // Iterate over the pages to concatenate the bodies.
+ err := p.EachPage(func(page Page) (bool, error) {
+ b := page.GetBody().(map[string]interface{})
+ for k := range b {
+ // If it's a linked page, we don't want the `links`, we want the other one.
+ if k != "links" {
+ key = k
+ }
+ }
+ pagesSlice = append(pagesSlice, b[key].([]interface{})...)
+ return true, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Set body to value of type `map[string]interface{}`
+ body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice)))
+ body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice))
+
+ case []byte:
+ // Iterate over the pages to concatenate the bodies.
+ err := p.EachPage(func(page Page) (bool, error) {
+ b := page.GetBody().([]byte)
+ pagesSlice = append(pagesSlice, b)
+ // seperate pages with a comma
+ pagesSlice = append(pagesSlice, []byte{10})
+ return true, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Remove the last comma.
+ pagesSlice = pagesSlice[:len(pagesSlice)-1]
+ var b []byte
+ // Combine the slice of slices in to a single slice.
+ for _, slice := range pagesSlice {
+ b = append(b, slice.([]byte)...)
+ }
+ // Set body to value of type `bytes`.
+ body = reflect.New(reflect.TypeOf(b)).Elem()
+ body.SetBytes(b)
+
+ default:
+ return nil, fmt.Errorf("Page body has unrecognized type.")
+ }
+
+ // Each `Extract*` function is expecting a specific type of page coming back,
+ // otherwise the type assertion in those functions will fail. PageType is needed
+ // to create a type in this method that has the same type that the `Extract*`
+ // function is expecting and set the Body of that object to the concatenated
+ // pages.
+ page := reflect.New(reflect.TypeOf(p.PageType))
+ page.Elem().FieldByName("Body").Set(body)
+
+ // Type assert the page to a Page interface so that the type assertion in the
+ // `Extract*` methods will work.
+ return page.Elem().Interface().(Page), err
+}
diff --git a/pagination/single.go b/pagination/single.go
index 4dd3c5c..f78d4ab 100644
--- a/pagination/single.go
+++ b/pagination/single.go
@@ -7,3 +7,9 @@
func (current SinglePageBase) NextPageURL() (string, error) {
return "", nil
}
+
+// GetBody returns the single page's body. This method is needed to satisfy the
+// Page interface.
+func (current SinglePageBase) GetBody() interface{} {
+ return current.Body
+}
diff --git a/pagination/single_test.go b/pagination/single_test.go
index 8817d57..7c07dfe 100644
--- a/pagination/single_test.go
+++ b/pagination/single_test.go
@@ -69,3 +69,17 @@
testhelper.CheckNoErr(t, err)
testhelper.CheckEquals(t, 1, callCount)
}
+
+func TestAllPagesSingle(t *testing.T) {
+ pager := setupSinglePaged()
+ defer testhelper.TeardownHTTP()
+
+ pager.PageType = SinglePageResult{}
+ page, err := pager.AllPages()
+ testhelper.AssertNoErr(t, err)
+
+ expected := []int{1, 2, 3}
+ actual, err := ExtractSingleInts(page)
+ testhelper.AssertNoErr(t, err)
+ testhelper.CheckDeepEquals(t, expected, actual)
+}