github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/pagination/linked.go (about) 1 package pagination 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 8 "github.com/opentelekomcloud/gophertelekomcloud" 9 "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" 10 ) 11 12 // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. 13 type LinkedPageBase struct { 14 PageResult 15 16 // LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer. 17 // If any link along the path is missing, an empty URL will be returned. 18 // If any link results in an unexpected value type, an error will be returned. 19 // When left as "nil", []string{"links", "next"} will be used as a default. 20 LinkPath []string 21 } 22 23 // NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present. 24 // It assumes that the links are available in a "links" element of the top-level response object. 25 // If this is not the case, override NextPageURL on your result type. 26 func (current LinkedPageBase) NextPageURL() (string, error) { 27 var path []string 28 var key string 29 30 if current.LinkPath == nil { 31 path = []string{"links", "next"} 32 } else { 33 path = current.LinkPath 34 } 35 36 submap := make(map[string]any) 37 38 err := extract.Into(bytes.NewReader(current.Body), &submap) 39 if err != nil { 40 err := golangsdk.ErrUnexpectedType{} 41 err.Expected = "map[string]any" 42 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) 43 return "", err 44 } 45 46 for { 47 key, path = path[0], path[1:] 48 49 value, ok := submap[key] 50 if !ok { 51 return "", nil 52 } 53 54 if len(path) > 0 { 55 submap, ok = value.(map[string]any) 56 if !ok { 57 err := golangsdk.ErrUnexpectedType{} 58 err.Expected = "map[string]any" 59 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) 60 return "", err 61 } 62 } else { 63 if value == nil { 64 // Actual null element. 65 return "", nil 66 } 67 68 url, ok := value.(string) 69 if !ok { 70 err := golangsdk.ErrUnexpectedType{} 71 err.Expected = "string" 72 err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) 73 return "", err 74 } 75 76 return url, nil 77 } 78 } 79 } 80 81 // IsEmpty satisfies the IsEmpty method of the Page interface 82 func (current LinkedPageBase) IsEmpty() (bool, error) { 83 body, err := current.GetBodyAsSlice() 84 if err != nil { 85 return false, fmt.Errorf("error converting page body to slice: %w", err) 86 } 87 88 return len(body) == 0, nil 89 } 90 91 // GetBody returns the linked page's body. This method is needed to satisfy the 92 // Page interface. 93 func (current LinkedPageBase) GetBody() []byte { 94 return current.Body 95 } 96 97 // WrapNextPageURL function use makerID to warp next page url,it returns the full url for request. 98 func (current LinkedPageBase) WrapNextPageURL(markerID string) (string, error) { 99 limit := current.URL.Query().Get("limit") 100 101 if limit == "" { 102 return "", nil 103 } 104 105 q := current.URL.Query() 106 107 q.Set("marker", markerID) 108 current.URL.RawQuery = q.Encode() 109 return current.URL.String(), nil 110 }