github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/pkg/cmd/api/pagination.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "net/url" 9 "regexp" 10 "strings" 11 ) 12 13 var linkRE = regexp.MustCompile(`<([^>]+)>;\s*rel="([^"]+)"`) 14 15 func findNextPage(resp *http.Response) (string, bool) { 16 for _, m := range linkRE.FindAllStringSubmatch(resp.Header.Get("Link"), -1) { 17 if len(m) >= 2 && m[2] == "next" { 18 return m[1], true 19 } 20 } 21 return "", false 22 } 23 24 func findEndCursor(r io.Reader) string { 25 dec := json.NewDecoder(r) 26 27 var idx int 28 var stack []json.Delim 29 var lastKey string 30 var contextKey string 31 32 var endCursor string 33 var hasNextPage bool 34 var foundEndCursor bool 35 var foundNextPage bool 36 37 loop: 38 for { 39 t, err := dec.Token() 40 if err == io.EOF { 41 break 42 } 43 if err != nil { 44 return "" 45 } 46 47 switch tt := t.(type) { 48 case json.Delim: 49 switch tt { 50 case '{', '[': 51 stack = append(stack, tt) 52 contextKey = lastKey 53 idx = 0 54 case '}', ']': 55 stack = stack[:len(stack)-1] 56 contextKey = "" 57 idx = 0 58 } 59 default: 60 isKey := len(stack) > 0 && stack[len(stack)-1] == '{' && idx%2 == 0 61 idx++ 62 63 switch tt := t.(type) { 64 case string: 65 if isKey { 66 lastKey = tt 67 } else if contextKey == "pageInfo" && lastKey == "endCursor" { 68 endCursor = tt 69 foundEndCursor = true 70 if foundNextPage { 71 break loop 72 } 73 } 74 case bool: 75 if contextKey == "pageInfo" && lastKey == "hasNextPage" { 76 hasNextPage = tt 77 foundNextPage = true 78 if foundEndCursor { 79 break loop 80 } 81 } 82 } 83 } 84 } 85 86 if hasNextPage { 87 return endCursor 88 } 89 return "" 90 } 91 92 func addPerPage(p string, perPage int, params map[string]interface{}) string { 93 if _, hasPerPage := params["per_page"]; hasPerPage { 94 return p 95 } 96 97 idx := strings.IndexRune(p, '?') 98 sep := "?" 99 100 if idx >= 0 { 101 if qp, err := url.ParseQuery(p[idx+1:]); err == nil && qp.Get("per_page") != "" { 102 return p 103 } 104 sep = "&" 105 } 106 107 return fmt.Sprintf("%s%sper_page=%d", p, sep, perPage) 108 }