github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/pr/list/http.go (about) 1 package list 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "github.com/ungtb10d/cli/v2/api" 8 "github.com/ungtb10d/cli/v2/internal/ghrepo" 9 prShared "github.com/ungtb10d/cli/v2/pkg/cmd/pr/shared" 10 ) 11 12 func shouldUseSearch(filters prShared.FilterOptions) bool { 13 return filters.Draft != nil || filters.Author != "" || filters.Assignee != "" || filters.Search != "" || len(filters.Labels) > 0 14 } 15 16 func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) { 17 if shouldUseSearch(filters) { 18 return searchPullRequests(httpClient, repo, filters, limit) 19 } 20 21 type response struct { 22 Repository struct { 23 PullRequests struct { 24 Nodes []api.PullRequest 25 PageInfo struct { 26 HasNextPage bool 27 EndCursor string 28 } 29 TotalCount int 30 } 31 } 32 } 33 34 fragment := fmt.Sprintf("fragment pr on PullRequest{%s}", api.PullRequestGraphQL(filters.Fields)) 35 query := fragment + ` 36 query PullRequestList( 37 $owner: String!, 38 $repo: String!, 39 $limit: Int!, 40 $endCursor: String, 41 $baseBranch: String, 42 $headBranch: String, 43 $state: [PullRequestState!] = OPEN 44 ) { 45 repository(owner: $owner, name: $repo) { 46 pullRequests( 47 states: $state, 48 baseRefName: $baseBranch, 49 headRefName: $headBranch, 50 first: $limit, 51 after: $endCursor, 52 orderBy: {field: CREATED_AT, direction: DESC} 53 ) { 54 totalCount 55 nodes { 56 ...pr 57 } 58 pageInfo { 59 hasNextPage 60 endCursor 61 } 62 } 63 } 64 }` 65 66 pageLimit := min(limit, 100) 67 variables := map[string]interface{}{ 68 "owner": repo.RepoOwner(), 69 "repo": repo.RepoName(), 70 } 71 72 switch filters.State { 73 case "open": 74 variables["state"] = []string{"OPEN"} 75 case "closed": 76 variables["state"] = []string{"CLOSED", "MERGED"} 77 case "merged": 78 variables["state"] = []string{"MERGED"} 79 case "all": 80 variables["state"] = []string{"OPEN", "CLOSED", "MERGED"} 81 default: 82 return nil, fmt.Errorf("invalid state: %s", filters.State) 83 } 84 85 if filters.BaseBranch != "" { 86 variables["baseBranch"] = filters.BaseBranch 87 } 88 if filters.HeadBranch != "" { 89 variables["headBranch"] = filters.HeadBranch 90 } 91 92 res := api.PullRequestAndTotalCount{} 93 var check = make(map[int]struct{}) 94 client := api.NewClientFromHTTP(httpClient) 95 96 loop: 97 for { 98 variables["limit"] = pageLimit 99 var data response 100 err := client.GraphQL(repo.RepoHost(), query, variables, &data) 101 if err != nil { 102 return nil, err 103 } 104 prData := data.Repository.PullRequests 105 res.TotalCount = prData.TotalCount 106 107 for _, pr := range prData.Nodes { 108 if _, exists := check[pr.Number]; exists && pr.Number > 0 { 109 continue 110 } 111 check[pr.Number] = struct{}{} 112 113 res.PullRequests = append(res.PullRequests, pr) 114 if len(res.PullRequests) == limit { 115 break loop 116 } 117 } 118 119 if prData.PageInfo.HasNextPage { 120 variables["endCursor"] = prData.PageInfo.EndCursor 121 pageLimit = min(pageLimit, limit-len(res.PullRequests)) 122 } else { 123 break 124 } 125 } 126 127 return &res, nil 128 } 129 130 func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) { 131 type response struct { 132 Search struct { 133 Nodes []api.PullRequest 134 PageInfo struct { 135 HasNextPage bool 136 EndCursor string 137 } 138 IssueCount int 139 } 140 } 141 142 fragment := fmt.Sprintf("fragment pr on PullRequest{%s}", api.PullRequestGraphQL(filters.Fields)) 143 query := fragment + ` 144 query PullRequestSearch( 145 $q: String!, 146 $limit: Int!, 147 $endCursor: String, 148 ) { 149 search(query: $q, type: ISSUE, first: $limit, after: $endCursor) { 150 issueCount 151 nodes { 152 ...pr 153 } 154 pageInfo { 155 hasNextPage 156 endCursor 157 } 158 } 159 }` 160 161 filters.Repo = ghrepo.FullName(repo) 162 filters.Entity = "pr" 163 q := prShared.SearchQueryBuild(filters) 164 165 pageLimit := min(limit, 100) 166 variables := map[string]interface{}{"q": q} 167 168 res := api.PullRequestAndTotalCount{SearchCapped: limit > 1000} 169 var check = make(map[int]struct{}) 170 client := api.NewClientFromHTTP(httpClient) 171 172 loop: 173 for { 174 variables["limit"] = pageLimit 175 var data response 176 err := client.GraphQL(repo.RepoHost(), query, variables, &data) 177 if err != nil { 178 return nil, err 179 } 180 prData := data.Search 181 res.TotalCount = prData.IssueCount 182 183 for _, pr := range prData.Nodes { 184 if _, exists := check[pr.Number]; exists && pr.Number > 0 { 185 continue 186 } 187 check[pr.Number] = struct{}{} 188 189 res.PullRequests = append(res.PullRequests, pr) 190 if len(res.PullRequests) == limit { 191 break loop 192 } 193 } 194 195 if prData.PageInfo.HasNextPage { 196 variables["endCursor"] = prData.PageInfo.EndCursor 197 pageLimit = min(pageLimit, limit-len(res.PullRequests)) 198 } else { 199 break 200 } 201 } 202 203 return &res, nil 204 } 205 206 func min(a, b int) int { 207 if a < b { 208 return a 209 } 210 return b 211 }