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