cli/pkg/cmd/pr/list/http.go
Mislav Marohnić 210d9dff20 Remove implicit sort:created-desc sort clause for pr list
This is the default sort mode for issues, so it's not needed to
explicitly set it. Furthermore, the user can specify their own sort mode
through the `--search` option.
2021-03-25 13:44:07 +01:00

246 lines
5 KiB
Go

package list
import (
"fmt"
"net/http"
"github.com/cli/cli/api"
"github.com/cli/cli/internal/ghrepo"
prShared "github.com/cli/cli/pkg/cmd/pr/shared"
"github.com/cli/cli/pkg/githubsearch"
)
const fragment = `fragment pr on PullRequest {
number
title
state
url
headRefName
headRepositoryOwner {
login
}
isCrossRepository
isDraft
}`
func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
if filters.Author != "" || filters.Assignee != "" || filters.Search != "" {
return searchPullRequests(httpClient, repo, filters, limit)
}
type response struct {
Repository struct {
PullRequests struct {
Nodes []api.PullRequest
PageInfo struct {
HasNextPage bool
EndCursor string
}
TotalCount int
}
}
}
query := fragment + `
query PullRequestList(
$owner: String!,
$repo: String!,
$limit: Int!,
$endCursor: String,
$baseBranch: String,
$labels: [String!],
$state: [PullRequestState!] = OPEN
) {
repository(owner: $owner, name: $repo) {
pullRequests(
states: $state,
baseRefName: $baseBranch,
labels: $labels,
first: $limit,
after: $endCursor,
orderBy: {field: CREATED_AT, direction: DESC}
) {
totalCount
nodes {
...pr
}
pageInfo {
hasNextPage
endCursor
}
}
}
}`
pageLimit := min(limit, 100)
variables := map[string]interface{}{
"owner": repo.RepoOwner(),
"repo": repo.RepoName(),
"labels": filters.Labels,
}
switch filters.State {
case "open":
variables["state"] = []string{"OPEN"}
case "closed":
variables["state"] = []string{"CLOSED", "MERGED"}
case "merged":
variables["state"] = []string{"MERGED"}
case "all":
variables["state"] = []string{"OPEN", "CLOSED", "MERGED"}
default:
return nil, fmt.Errorf("invalid state: %s", filters.State)
}
if filters.BaseBranch != "" {
variables["baseBranch"] = filters.BaseBranch
}
res := api.PullRequestAndTotalCount{}
var check = make(map[int]struct{})
client := api.NewClientFromHTTP(httpClient)
loop:
for {
variables["limit"] = pageLimit
var data response
err := client.GraphQL(repo.RepoHost(), query, variables, &data)
if err != nil {
return nil, err
}
prData := data.Repository.PullRequests
res.TotalCount = prData.TotalCount
for _, pr := range prData.Nodes {
if _, exists := check[pr.Number]; exists {
continue
}
check[pr.Number] = struct{}{}
res.PullRequests = append(res.PullRequests, pr)
if len(res.PullRequests) == limit {
break loop
}
}
if prData.PageInfo.HasNextPage {
variables["endCursor"] = prData.PageInfo.EndCursor
pageLimit = min(pageLimit, limit-len(res.PullRequests))
} else {
break
}
}
return &res, nil
}
func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
if len(filters.Labels) > 1 {
return nil, fmt.Errorf("multiple labels with --assignee are not supported")
}
type response struct {
Search struct {
Nodes []api.PullRequest
PageInfo struct {
HasNextPage bool
EndCursor string
}
IssueCount int
}
}
query := fragment + `
query PullRequestSearch(
$q: String!,
$limit: Int!,
$endCursor: String,
) {
search(query: $q, type: ISSUE, first: $limit, after: $endCursor) {
issueCount
nodes {
...pr
}
pageInfo {
hasNextPage
endCursor
}
}
}`
q := githubsearch.NewQuery()
q.SetType(githubsearch.PullRequest)
q.InRepository(ghrepo.FullName(repo))
q.AddQuery(filters.Search)
switch filters.State {
case "open":
q.SetState(githubsearch.Open)
case "closed":
q.SetState(githubsearch.Closed)
case "merged":
q.SetState(githubsearch.Merged)
}
if filters.Author != "" {
q.AuthoredBy(filters.Author)
}
if filters.Assignee != "" {
q.AssignedTo(filters.Assignee)
}
if len(filters.Labels) > 0 {
q.AddLabel(filters.Labels[0])
}
if filters.BaseBranch != "" {
q.SetBaseBranch(filters.BaseBranch)
}
pageLimit := min(limit, 100)
variables := map[string]interface{}{
"q": q.String(),
}
res := api.PullRequestAndTotalCount{}
var check = make(map[int]struct{})
client := api.NewClientFromHTTP(httpClient)
loop:
for {
variables["limit"] = pageLimit
var data response
err := client.GraphQL(repo.RepoHost(), query, variables, &data)
if err != nil {
return nil, err
}
prData := data.Search
res.TotalCount = prData.IssueCount
for _, pr := range prData.Nodes {
if _, exists := check[pr.Number]; exists {
continue
}
check[pr.Number] = struct{}{}
res.PullRequests = append(res.PullRequests, pr)
if len(res.PullRequests) == limit {
break loop
}
}
if prData.PageInfo.HasNextPage {
variables["endCursor"] = prData.PageInfo.EndCursor
pageLimit = min(pageLimit, limit-len(res.PullRequests))
} else {
break
}
}
return &res, nil
}
func min(a, b int) int {
if a < b {
return a
}
return b
}