blob: 3656fb7f8f476bcde63c1fc37c9692bf7da050ab [file] [log] [blame]
Ash Wilsonc8e68872014-09-16 10:36:56 -04001package pagination
2
Jon Perrittc7d828e2016-02-25 03:06:33 -06003import (
4 "fmt"
5 "reflect"
Jon Perritt80251972016-03-09 00:32:30 -06006
7 "github.com/gophercloud/gophercloud"
Jon Perrittc7d828e2016-02-25 03:06:33 -06008)
Ash Wilsonc8e68872014-09-16 10:36:56 -04009
10// LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result.
Ash Wilsonfc55c822014-09-25 13:18:16 -040011type LinkedPageBase struct {
Ash Wilsonb8b16f82014-10-20 10:19:49 -040012 PageResult
Ash Wilsonfc55c822014-09-25 13:18:16 -040013
14 // LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer.
15 // If any link along the path is missing, an empty URL will be returned.
16 // If any link results in an unexpected value type, an error will be returned.
17 // When left as "nil", []string{"links", "next"} will be used as a default.
18 LinkPath []string
19}
Ash Wilsonc8e68872014-09-16 10:36:56 -040020
21// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present.
22// It assumes that the links are available in a "links" element of the top-level response object.
23// If this is not the case, override NextPageURL on your result type.
24func (current LinkedPageBase) NextPageURL() (string, error) {
Ash Wilsonfc55c822014-09-25 13:18:16 -040025 var path []string
26 var key string
27
28 if current.LinkPath == nil {
29 path = []string{"links", "next"}
30 } else {
31 path = current.LinkPath
Ash Wilsonc8e68872014-09-16 10:36:56 -040032 }
33
Ash Wilsonfc55c822014-09-25 13:18:16 -040034 submap, ok := current.Body.(map[string]interface{})
35 if !ok {
Jon Perritt80251972016-03-09 00:32:30 -060036 err := gophercloud.ErrUnexpectedType{}
37 err.Expected = "map[string]interface{}"
38 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
39 return "", err
Ash Wilsonc8e68872014-09-16 10:36:56 -040040 }
41
Ash Wilsonfc55c822014-09-25 13:18:16 -040042 for {
43 key, path = path[0], path[1:len(path)]
Ash Wilsonc8e68872014-09-16 10:36:56 -040044
Ash Wilsonfc55c822014-09-25 13:18:16 -040045 value, ok := submap[key]
46 if !ok {
47 return "", nil
48 }
49
Ash Wilsonfc55c822014-09-25 13:18:16 -040050 if len(path) > 0 {
51 submap, ok = value.(map[string]interface{})
52 if !ok {
Jon Perritt80251972016-03-09 00:32:30 -060053 err := gophercloud.ErrUnexpectedType{}
54 err.Expected = "map[string]interface{}"
55 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
56 return "", err
Ash Wilsonfc55c822014-09-25 13:18:16 -040057 }
58 } else {
59 if value == nil {
60 // Actual null element.
61 return "", nil
62 }
63
64 url, ok := value.(string)
65 if !ok {
Jon Perritt80251972016-03-09 00:32:30 -060066 err := gophercloud.ErrUnexpectedType{}
67 err.Expected = "string"
68 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value))
69 return "", err
Ash Wilsonfc55c822014-09-25 13:18:16 -040070 }
71
72 return url, nil
73 }
74 }
Ash Wilsonc8e68872014-09-16 10:36:56 -040075}
Jon Perrittdb319f12015-02-17 19:32:40 -070076
Jon Perritt80251972016-03-09 00:32:30 -060077// IsEmpty satisifies the IsEmpty method of the Page interface
Jon Perrittc7d828e2016-02-25 03:06:33 -060078func (current LinkedPageBase) IsEmpty() (bool, error) {
79 if b, ok := current.Body.([]interface{}); ok {
80 return len(b) == 0, nil
81 }
Jon Perritt80251972016-03-09 00:32:30 -060082 err := gophercloud.ErrUnexpectedType{}
83 err.Expected = "[]interface{}"
84 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body))
85 return true, err
Jon Perrittc7d828e2016-02-25 03:06:33 -060086}
87
Jon Perrittdb319f12015-02-17 19:32:40 -070088// GetBody returns the linked page's body. This method is needed to satisfy the
89// Page interface.
90func (current LinkedPageBase) GetBody() interface{} {
91 return current.Body
92}