cli/pkg/cmd/pr/list/http.go
Mislav Marohnić abe452bb19 Add --json export flag for issues and pull requests
The `--json` flag accepts a list of GraphQL fields to query for and
output in JSON format. To get the list of available flags, run the
command with a blank value for `--json`. Additional `--jq` and
`--template` flags are available just like in `gh api`.
2021-04-13 20:29:31 +02:00

228 lines
4.9 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"
)
func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
if filters.Author != "" || filters.Assignee != "" || filters.Search != "" || len(filters.Labels) > 0 {
return searchPullRequests(httpClient, repo, filters, limit)
}
type response struct {
Repository struct {
PullRequests struct {
Nodes []api.PullRequest
PageInfo struct {
HasNextPage bool
EndCursor string
}
TotalCount int
}
}
}
fragment := fmt.Sprintf("fragment pr on PullRequest{%s}", api.PullRequestGraphQL(filters.Fields))
query := fragment + `
query PullRequestList(
$owner: String!,
$repo: String!,
$limit: Int!,
$endCursor: String,
$baseBranch: String,
$state: [PullRequestState!] = OPEN
) {
repository(owner: $owner, name: $repo) {
pullRequests(
states: $state,
baseRefName: $baseBranch,
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(),
}
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 && pr.Number > 0 {
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) {
type response struct {
Search struct {
Nodes []api.PullRequest
PageInfo struct {
HasNextPage bool
EndCursor string
}
IssueCount int
}
}
fragment := fmt.Sprintf("fragment pr on PullRequest{%s}", api.PullRequestGraphQL(filters.Fields))
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)
}
for _, label := range filters.Labels {
q.AddLabel(label)
}
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 && pr.Number > 0 {
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
}