github.com/chnsz/golangsdk@v0.0.0-20240506093406-85a3fbfa605b/pagination/offset.go (about) 1 package pagination 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/chnsz/golangsdk" 9 ) 10 11 // OffsetPage is used for paging queries based on offset and limit. 12 type OffsetPage interface { 13 Page 14 15 NextOffset() int 16 } 17 18 // OffsetPageBase is used for paging queries based on offset and limit. 19 // DefaultOffset and DefaultLimit are used to calculate the next offset 20 type OffsetPageBase struct { 21 PageResult 22 23 DefaultOffset int 24 DefaultLimit int 25 } 26 27 // NextOffset returns offset of the next element of the page. 28 func (current OffsetPageBase) NextOffset() int { 29 q := current.URL.Query() 30 offset, _ := strconv.Atoi(q.Get("offset")) 31 if offset == 0 { 32 offset = current.DefaultOffset 33 } 34 35 // get `limit` according to the following priority: 36 // query path > current page size > DefaultLimit 37 limit, _ := strconv.Atoi(q.Get("limit")) 38 if limit == 0 { 39 count, err := current.getPageSize() 40 if err != nil { 41 limit = current.DefaultLimit 42 } 43 limit = count 44 // save `limit` in query path 45 q.Set("limit", strconv.Itoa(limit)) 46 } 47 48 return offset + limit 49 } 50 51 // NextPageURL generates the URL for the page of results after this one. 52 func (current OffsetPageBase) NextPageURL() (string, error) { 53 next := current.NextOffset() 54 if next == 0 { 55 return "", nil 56 } 57 58 currentURL := current.URL 59 q := currentURL.Query() 60 q.Set("offset", strconv.Itoa(next)) 61 currentURL.RawQuery = q.Encode() 62 63 return currentURL.String(), nil 64 } 65 66 // IsEmpty satisifies the IsEmpty method of the Page interface. 67 func (current OffsetPageBase) IsEmpty() (bool, error) { 68 if pb, ok := current.Body.(map[string]interface{}); ok { 69 for k, v := range pb { 70 // ignore xxx_links 71 if !strings.HasSuffix(k, "links") { 72 // check the field's type. we only want []interface{} (which is really []map[string]interface{}) 73 switch vt := v.(type) { 74 case []interface{}: 75 return len(vt) == 0, nil 76 } 77 } 78 } 79 } 80 if pb, ok := current.Body.([]interface{}); ok { 81 return len(pb) == 0, nil 82 } 83 84 err := golangsdk.ErrUnexpectedType{} 85 err.Expected = "map[string]interface{}/[]interface{}" 86 err.Actual = fmt.Sprintf("%T", current.Body) 87 return true, err 88 } 89 90 // GetBody returns the page's body. This method is needed to satisfy the Page interface. 91 func (current OffsetPageBase) GetBody() interface{} { 92 return current.Body 93 } 94 95 // getPageSize calculates the count of items in an offset page 96 func (current OffsetPageBase) getPageSize() (int, error) { 97 var pageSize int 98 99 switch pb := current.Body.(type) { 100 case map[string]interface{}: 101 for k, v := range pb { 102 // ignore xxx_links 103 if !strings.HasSuffix(k, "links") { 104 // check the field's type. we only want []interface{} (which is really []map[string]interface{}) 105 switch vt := v.(type) { 106 case []interface{}: 107 pageSize = len(vt) 108 break 109 } 110 } 111 } 112 case []interface{}: 113 pageSize = len(pb) 114 default: 115 err := golangsdk.ErrUnexpectedType{} 116 err.Expected = "map[string]interface{}/[]interface{}" 117 err.Actual = fmt.Sprintf("%T", pb) 118 return 0, err 119 } 120 121 return pageSize, nil 122 }