refactor(issue/pr list): support advanced issue search
Signed-off-by: Babak K. Shandiz <babakks@github.com>
This commit is contained in:
parent
04cce6b35e
commit
6d148400a8
9 changed files with 158 additions and 71 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
)
|
||||
|
|
@ -112,7 +113,12 @@ loop:
|
|||
return &res, nil
|
||||
}
|
||||
|
||||
func searchIssues(client *api.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.IssuesAndTotalCount, error) {
|
||||
func searchIssues(client *api.Client, detector fd.Detector, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.IssuesAndTotalCount, error) {
|
||||
features, err := detector.SearchFeatures()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fragments := fmt.Sprintf("fragment issue on Issue {%s}", api.IssueGraphQL(filters.Fields))
|
||||
query := fragments +
|
||||
`query IssueSearch($repo: String!, $owner: String!, $type: SearchType!, $limit: Int, $after: String, $query: String!) {
|
||||
|
|
@ -143,18 +149,27 @@ func searchIssues(client *api.Client, repo ghrepo.Interface, filters prShared.Fi
|
|||
}
|
||||
}
|
||||
|
||||
filters.Repo = ghrepo.FullName(repo)
|
||||
filters.Entity = "issue"
|
||||
q := prShared.SearchQueryBuild(filters)
|
||||
|
||||
perPage := min(limit, 100)
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"owner": repo.RepoOwner(),
|
||||
"repo": repo.RepoName(),
|
||||
"type": "ISSUE",
|
||||
"limit": perPage,
|
||||
"query": q,
|
||||
}
|
||||
|
||||
filters.Repo = ghrepo.FullName(repo)
|
||||
filters.Entity = "issue"
|
||||
|
||||
if features.AdvancedIssueSearchAPI {
|
||||
variables["query"] = prShared.SearchQueryBuild(filters, true)
|
||||
if features.AdvancedIssueSearchAPIOptIn {
|
||||
variables["type"] = "ISSUE_ADVANCED"
|
||||
} else {
|
||||
variables["type"] = "ISSUE"
|
||||
}
|
||||
} else {
|
||||
variables["query"] = prShared.SearchQueryBuild(filters, false)
|
||||
variables["type"] = "ISSUE"
|
||||
}
|
||||
|
||||
ic := api.IssuesAndTotalCount{SearchCapped: limit > 1000}
|
||||
|
|
|
|||
|
|
@ -165,8 +165,15 @@ func listRun(opts *ListOptions) error {
|
|||
isTerminal := opts.IO.IsStdoutTTY()
|
||||
|
||||
if opts.WebMode {
|
||||
searchFeatures, err := opts.Detector.SearchFeatures()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
advancedSyntaxSupported := searchFeatures.AdvancedIssueSearchAPI && searchFeatures.AdvancedIssueSearchWebInIssuesTab
|
||||
|
||||
issueListURL := ghrepo.GenerateRepoURL(baseRepo, "issues")
|
||||
openURL, err := prShared.ListURLWithQuery(issueListURL, filterOptions)
|
||||
openURL, err := prShared.ListURLWithQuery(issueListURL, filterOptions, advancedSyntaxSupported)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -181,7 +188,7 @@ func listRun(opts *ListOptions) error {
|
|||
filterOptions.Fields = opts.Exporter.Fields()
|
||||
}
|
||||
|
||||
listResult, err := issueList(httpClient, baseRepo, filterOptions, opts.LimitResults)
|
||||
listResult, err := issueList(httpClient, opts.Detector, baseRepo, filterOptions, opts.LimitResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -212,7 +219,7 @@ func listRun(opts *ListOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func issueList(client *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.IssuesAndTotalCount, error) {
|
||||
func issueList(client *http.Client, detector fd.Detector, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.IssuesAndTotalCount, error) {
|
||||
apiClient := api.NewClientFromHTTP(client)
|
||||
|
||||
if filters.Search != "" || len(filters.Labels) > 0 || filters.Milestone != "" {
|
||||
|
|
@ -224,7 +231,7 @@ func issueList(client *http.Client, repo ghrepo.Interface, filters prShared.Filt
|
|||
filters.Milestone = milestone.Title
|
||||
}
|
||||
|
||||
return searchIssues(apiClient, repo, filters, limit)
|
||||
return searchIssues(apiClient, detector, repo, filters, limit)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/run"
|
||||
|
|
@ -210,6 +211,7 @@ func TestIssueList_web(t *testing.T) {
|
|||
BaseRepo: func() (ghrepo.Interface, error) {
|
||||
return ghrepo.New("OWNER", "REPO"), nil
|
||||
},
|
||||
Detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
WebMode: true,
|
||||
State: "all",
|
||||
Assignee: "peter",
|
||||
|
|
@ -230,9 +232,10 @@ func TestIssueList_web(t *testing.T) {
|
|||
|
||||
func Test_issueList(t *testing.T) {
|
||||
type args struct {
|
||||
repo ghrepo.Interface
|
||||
filters prShared.FilterOptions
|
||||
limit int
|
||||
detector fd.Detector
|
||||
repo ghrepo.Interface
|
||||
filters prShared.FilterOptions
|
||||
limit int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -243,8 +246,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "default",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -270,8 +274,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "milestone by number",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -309,8 +314,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "milestone by title",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -341,8 +347,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "@me syntax",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -377,8 +384,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "@me with search",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -412,8 +420,9 @@ func Test_issueList(t *testing.T) {
|
|||
{
|
||||
name: "with labels",
|
||||
args: args{
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
limit: 30,
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
filters: prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
State: "open",
|
||||
|
|
@ -450,7 +459,7 @@ func Test_issueList(t *testing.T) {
|
|||
tt.httpStubs(httpreg)
|
||||
}
|
||||
client := &http.Client{Transport: httpreg}
|
||||
_, err := issueList(client, tt.args.repo, tt.args.filters, tt.args.limit)
|
||||
_, err := issueList(client, tt.args.detector, tt.args.repo, tt.args.filters, tt.args.limit)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
|
|
@ -507,6 +516,7 @@ func TestIssueList_withProjectItems(t *testing.T) {
|
|||
client := &http.Client{Transport: reg}
|
||||
issuesAndTotalCount, err := issueList(
|
||||
client,
|
||||
fd.AdvancedIssueSearchUnsupported(),
|
||||
ghrepo.New("OWNER", "REPO"),
|
||||
prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
|
|
@ -581,6 +591,7 @@ func TestIssueList_Search_withProjectItems(t *testing.T) {
|
|||
client := &http.Client{Transport: reg}
|
||||
issuesAndTotalCount, err := issueList(
|
||||
client,
|
||||
fd.AdvancedIssueSearchUnsupported(),
|
||||
ghrepo.New("OWNER", "REPO"),
|
||||
prShared.FilterOptions{
|
||||
Entity: "issue",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
)
|
||||
|
|
@ -13,9 +14,9 @@ func shouldUseSearch(filters prShared.FilterOptions) bool {
|
|||
return filters.Draft != nil || filters.Author != "" || filters.Assignee != "" || filters.Search != "" || len(filters.Labels) > 0
|
||||
}
|
||||
|
||||
func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
|
||||
func listPullRequests(httpClient *http.Client, detector fd.Detector, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
|
||||
if shouldUseSearch(filters) {
|
||||
return searchPullRequests(httpClient, repo, filters, limit)
|
||||
return searchPullRequests(httpClient, detector, repo, filters, limit)
|
||||
}
|
||||
|
||||
return prShared.NewLister(httpClient).List(prShared.ListOptions{
|
||||
|
|
@ -28,7 +29,12 @@ func listPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters pr
|
|||
})
|
||||
}
|
||||
|
||||
func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
|
||||
func searchPullRequests(httpClient *http.Client, detector fd.Detector, repo ghrepo.Interface, filters prShared.FilterOptions, limit int) (*api.PullRequestAndTotalCount, error) {
|
||||
features, err := detector.SearchFeatures()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Search struct {
|
||||
Nodes []api.PullRequest
|
||||
|
|
@ -44,10 +50,11 @@ func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters
|
|||
query := fragment + `
|
||||
query PullRequestSearch(
|
||||
$q: String!,
|
||||
$type: SearchType!,
|
||||
$limit: Int!,
|
||||
$endCursor: String,
|
||||
) {
|
||||
search(query: $q, type: ISSUE, first: $limit, after: $endCursor) {
|
||||
search(query: $q, type: $type, first: $limit, after: $endCursor) {
|
||||
issueCount
|
||||
nodes {
|
||||
...pr
|
||||
|
|
@ -59,12 +66,24 @@ func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters
|
|||
}
|
||||
}`
|
||||
|
||||
variables := map[string]interface{}{}
|
||||
|
||||
filters.Repo = ghrepo.FullName(repo)
|
||||
filters.Entity = "pr"
|
||||
q := prShared.SearchQueryBuild(filters)
|
||||
|
||||
if features.AdvancedIssueSearchAPI {
|
||||
variables["q"] = prShared.SearchQueryBuild(filters, true)
|
||||
if features.AdvancedIssueSearchAPIOptIn {
|
||||
variables["type"] = "ISSUE_ADVANCED"
|
||||
} else {
|
||||
variables["type"] = "ISSUE"
|
||||
}
|
||||
} else {
|
||||
variables["q"] = prShared.SearchQueryBuild(filters, false)
|
||||
variables["type"] = "ISSUE"
|
||||
}
|
||||
|
||||
pageLimit := min(limit, 100)
|
||||
variables := map[string]interface{}{"q": q}
|
||||
|
||||
res := api.PullRequestAndTotalCount{SearchCapped: limit > 1000}
|
||||
var check = make(map[int]struct{})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
|
|
@ -12,9 +13,10 @@ import (
|
|||
|
||||
func Test_ListPullRequests(t *testing.T) {
|
||||
type args struct {
|
||||
repo ghrepo.Interface
|
||||
filters prShared.FilterOptions
|
||||
limit int
|
||||
detector fd.Detector
|
||||
repo ghrepo.Interface
|
||||
filters prShared.FilterOptions
|
||||
limit int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -25,8 +27,9 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
{
|
||||
name: "default",
|
||||
args: args{
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
filters: prShared.FilterOptions{
|
||||
State: "open",
|
||||
},
|
||||
|
|
@ -50,8 +53,9 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
{
|
||||
name: "closed",
|
||||
args: args{
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
filters: prShared.FilterOptions{
|
||||
State: "closed",
|
||||
},
|
||||
|
|
@ -75,8 +79,9 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
{
|
||||
name: "with labels",
|
||||
args: args{
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
filters: prShared.FilterOptions{
|
||||
State: "open",
|
||||
Labels: []string{"hello", "one world"},
|
||||
|
|
@ -88,6 +93,7 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
httpmock.GraphQLQuery(`{"data":{}}`, func(query string, vars map[string]interface{}) {
|
||||
want := map[string]interface{}{
|
||||
"q": `label:"one world" label:hello repo:OWNER/REPO state:open type:pr`,
|
||||
"type": "ISSUE",
|
||||
"limit": float64(30),
|
||||
}
|
||||
if !reflect.DeepEqual(vars, want) {
|
||||
|
|
@ -99,8 +105,9 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
{
|
||||
name: "with author",
|
||||
args: args{
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
filters: prShared.FilterOptions{
|
||||
State: "open",
|
||||
Author: "monalisa",
|
||||
|
|
@ -112,6 +119,7 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
httpmock.GraphQLQuery(`{"data":{}}`, func(query string, vars map[string]interface{}) {
|
||||
want := map[string]interface{}{
|
||||
"q": "author:monalisa repo:OWNER/REPO state:open type:pr",
|
||||
"type": "ISSUE",
|
||||
"limit": float64(30),
|
||||
}
|
||||
if !reflect.DeepEqual(vars, want) {
|
||||
|
|
@ -123,8 +131,9 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
{
|
||||
name: "with search",
|
||||
args: args{
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
detector: fd.AdvancedIssueSearchUnsupported(),
|
||||
repo: ghrepo.New("OWNER", "REPO"),
|
||||
limit: 30,
|
||||
filters: prShared.FilterOptions{
|
||||
State: "open",
|
||||
Search: "one world in:title",
|
||||
|
|
@ -136,6 +145,7 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
httpmock.GraphQLQuery(`{"data":{}}`, func(query string, vars map[string]interface{}) {
|
||||
want := map[string]interface{}{
|
||||
"q": "one world in:title repo:OWNER/REPO state:open type:pr",
|
||||
"type": "ISSUE",
|
||||
"limit": float64(30),
|
||||
}
|
||||
if !reflect.DeepEqual(vars, want) {
|
||||
|
|
@ -153,7 +163,7 @@ func Test_ListPullRequests(t *testing.T) {
|
|||
}
|
||||
httpClient := &http.Client{Transport: reg}
|
||||
|
||||
_, err := listPullRequests(httpClient, tt.args.repo, tt.args.filters, tt.args.limit)
|
||||
_, err := listPullRequests(httpClient, tt.args.detector, tt.args.repo, tt.args.filters, tt.args.limit)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListPullRequests() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/tableprinter"
|
||||
"github.com/cli/cli/v2/internal/text"
|
||||
|
|
@ -24,6 +25,7 @@ type ListOptions struct {
|
|||
IO *iostreams.IOStreams
|
||||
BaseRepo func() (ghrepo.Interface, error)
|
||||
Browser browser.Browser
|
||||
Detector fd.Detector
|
||||
|
||||
WebMode bool
|
||||
LimitResults int
|
||||
|
|
@ -142,6 +144,11 @@ func listRun(opts *ListOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if opts.Detector == nil {
|
||||
cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
|
||||
opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
|
||||
}
|
||||
|
||||
prState := strings.ToLower(opts.State)
|
||||
if prState == "open" && shared.QueryHasStateClause(opts.Search) {
|
||||
prState = ""
|
||||
|
|
@ -164,7 +171,11 @@ func listRun(opts *ListOptions) error {
|
|||
}
|
||||
if opts.WebMode {
|
||||
prListURL := ghrepo.GenerateRepoURL(baseRepo, "pulls")
|
||||
openURL, err := shared.ListURLWithQuery(prListURL, filters)
|
||||
|
||||
// TODO(babakks): As of August 2025, the advanced issue search syntax is
|
||||
// not supported in Pull Requests tab of repositories. When it's supported
|
||||
// we can change the argument to true.
|
||||
openURL, err := shared.ListURLWithQuery(prListURL, filters, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -175,7 +186,7 @@ func listRun(opts *ListOptions) error {
|
|||
return opts.Browser.Browse(openURL)
|
||||
}
|
||||
|
||||
listResult, err := listPullRequests(httpClient, baseRepo, filters, opts.LimitResults)
|
||||
listResult, err := listPullRequests(httpClient, opts.Detector, baseRepo, filters, opts.LimitResults)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/browser"
|
||||
fd "github.com/cli/cli/v2/internal/featuredetection"
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/internal/run"
|
||||
prShared "github.com/cli/cli/v2/pkg/cmd/pr/shared"
|
||||
|
|
@ -23,7 +24,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, error) {
|
||||
func runCommand(rt http.RoundTripper, detector fd.Detector, isTTY bool, cli string) (*test.CmdOut, error) {
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(isTTY)
|
||||
ios.SetStdinTTY(isTTY)
|
||||
|
|
@ -47,6 +48,7 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err
|
|||
|
||||
cmd := NewCmdList(factory, func(opts *ListOptions) error {
|
||||
opts.Now = fakeNow
|
||||
opts.Detector = detector
|
||||
return listRun(opts)
|
||||
})
|
||||
|
||||
|
|
@ -78,7 +80,7 @@ func TestPRList(t *testing.T) {
|
|||
|
||||
http.Register(httpmock.GraphQL(`query PullRequestList\b`), httpmock.FileResponse("./fixtures/prList.json"))
|
||||
|
||||
output, err := runCommand(http, true, "")
|
||||
output, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -101,7 +103,7 @@ func TestPRList_nontty(t *testing.T) {
|
|||
|
||||
http.Register(httpmock.GraphQL(`query PullRequestList\b`), httpmock.FileResponse("./fixtures/prList.json"))
|
||||
|
||||
output, err := runCommand(http, false, "")
|
||||
output, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -124,7 +126,7 @@ func TestPRList_filtering(t *testing.T) {
|
|||
assert.Equal(t, []interface{}{"OPEN", "CLOSED", "MERGED"}, params["state"].([]interface{}))
|
||||
}))
|
||||
|
||||
output, err := runCommand(http, true, `-s all`)
|
||||
output, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, `-s all`)
|
||||
assert.Error(t, err)
|
||||
|
||||
assert.Equal(t, "", output.String())
|
||||
|
|
@ -139,7 +141,7 @@ func TestPRList_filteringRemoveDuplicate(t *testing.T) {
|
|||
httpmock.GraphQL(`query PullRequestList\b`),
|
||||
httpmock.FileResponse("./fixtures/prListWithDuplicates.json"))
|
||||
|
||||
output, err := runCommand(http, true, "")
|
||||
output, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -162,7 +164,7 @@ func TestPRList_filteringClosed(t *testing.T) {
|
|||
assert.Equal(t, []interface{}{"CLOSED", "MERGED"}, params["state"].([]interface{}))
|
||||
}))
|
||||
|
||||
_, err := runCommand(http, true, `-s closed`)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, `-s closed`)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
|
@ -176,7 +178,7 @@ func TestPRList_filteringHeadBranch(t *testing.T) {
|
|||
assert.Equal(t, interface{}("bug-fix"), params["headBranch"])
|
||||
}))
|
||||
|
||||
_, err := runCommand(http, true, `-H bug-fix`)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, `-H bug-fix`)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +192,7 @@ func TestPRList_filteringAssignee(t *testing.T) {
|
|||
assert.Equal(t, `assignee:hubot base:develop is:merged label:"needs tests" repo:OWNER/REPO type:pr`, params["q"].(string))
|
||||
}))
|
||||
|
||||
_, err := runCommand(http, true, `-s merged -l "needs tests" -a hubot -B develop`)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, `-s merged -l "needs tests" -a hubot -B develop`)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +225,7 @@ func TestPRList_filteringDraft(t *testing.T) {
|
|||
assert.Equal(t, test.expectedQuery, params["q"].(string))
|
||||
}))
|
||||
|
||||
_, err := runCommand(http, true, test.cli)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, test.cli)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
@ -268,7 +270,7 @@ func TestPRList_filteringAuthor(t *testing.T) {
|
|||
assert.Equal(t, test.expectedQuery, params["q"].(string))
|
||||
}))
|
||||
|
||||
_, err := runCommand(http, true, test.cli)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, test.cli)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
@ -277,7 +279,7 @@ func TestPRList_filteringAuthor(t *testing.T) {
|
|||
func TestPRList_withInvalidLimitFlag(t *testing.T) {
|
||||
http := initFakeHTTP()
|
||||
defer http.Verify(t)
|
||||
_, err := runCommand(http, true, `--limit=0`)
|
||||
_, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, `--limit=0`)
|
||||
assert.EqualError(t, err, "invalid value for --limit: 0")
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +314,7 @@ func TestPRList_web(t *testing.T) {
|
|||
_, cmdTeardown := run.Stub()
|
||||
defer cmdTeardown(t)
|
||||
|
||||
output, err := runCommand(http, true, "--web "+test.cli)
|
||||
output, err := runCommand(http, fd.AdvancedIssueSearchUnsupported(), true, "--web "+test.cli)
|
||||
if err != nil {
|
||||
t.Errorf("error running command `pr list` with `--web` flag: %v", err)
|
||||
}
|
||||
|
|
@ -370,6 +372,7 @@ func TestPRList_withProjectItems(t *testing.T) {
|
|||
client := &http.Client{Transport: reg}
|
||||
prsAndTotalCount, err := listPullRequests(
|
||||
client,
|
||||
fd.AdvancedIssueSearchUnsupported(),
|
||||
ghrepo.New("OWNER", "REPO"),
|
||||
prShared.FilterOptions{
|
||||
Entity: "pr",
|
||||
|
|
@ -433,12 +436,14 @@ func TestPRList_Search_withProjectItems(t *testing.T) {
|
|||
require.Equal(t, map[string]interface{}{
|
||||
"limit": float64(30),
|
||||
"q": "just used to force the search API branch repo:OWNER/REPO state:open type:pr",
|
||||
"type": "ISSUE",
|
||||
}, params)
|
||||
}))
|
||||
|
||||
client := &http.Client{Transport: reg}
|
||||
prsAndTotalCount, err := listPullRequests(
|
||||
client,
|
||||
fd.AdvancedIssueSearchUnsupported(),
|
||||
ghrepo.New("OWNER", "REPO"),
|
||||
prShared.FilterOptions{
|
||||
Entity: "pr",
|
||||
|
|
|
|||
|
|
@ -186,20 +186,20 @@ func (opts *FilterOptions) IsDefault() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func ListURLWithQuery(listURL string, options FilterOptions) (string, error) {
|
||||
func ListURLWithQuery(listURL string, options FilterOptions, advancedIssueSearchSyntax bool) (string, error) {
|
||||
u, err := url.Parse(listURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
params := u.Query()
|
||||
params.Set("q", SearchQueryBuild(options))
|
||||
params.Set("q", SearchQueryBuild(options, advancedIssueSearchSyntax))
|
||||
u.RawQuery = params.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func SearchQueryBuild(options FilterOptions) string {
|
||||
func SearchQueryBuild(options FilterOptions, advancedIssueSearchSyntax bool) string {
|
||||
var is, state string
|
||||
switch options.State {
|
||||
case "open", "closed":
|
||||
|
|
@ -207,7 +207,7 @@ func SearchQueryBuild(options FilterOptions) string {
|
|||
case "merged":
|
||||
is = "merged"
|
||||
}
|
||||
q := search.Query{
|
||||
query := search.Query{
|
||||
Qualifiers: search.Qualifiers{
|
||||
Assignee: options.Assignee,
|
||||
Author: options.Author,
|
||||
|
|
@ -223,10 +223,18 @@ func SearchQueryBuild(options FilterOptions) string {
|
|||
Type: options.Entity,
|
||||
},
|
||||
}
|
||||
if options.Search != "" {
|
||||
return fmt.Sprintf("%s %s", options.Search, q.String())
|
||||
|
||||
var q string
|
||||
if advancedIssueSearchSyntax {
|
||||
q = query.AdvancedIssueSearchString()
|
||||
} else {
|
||||
q = query.String()
|
||||
}
|
||||
return q.String()
|
||||
|
||||
if options.Search != "" {
|
||||
return fmt.Sprintf("%s %s", options.Search, q)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func QueryHasStateClause(searchQuery string) bool {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ func Test_listURLWithQuery(t *testing.T) {
|
|||
falseBool := false
|
||||
|
||||
type args struct {
|
||||
listURL string
|
||||
options FilterOptions
|
||||
listURL string
|
||||
options FilterOptions
|
||||
advancedIssueSearchSyntax bool
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -101,7 +102,7 @@ func Test_listURLWithQuery(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ListURLWithQuery(tt.args.listURL, tt.args.options)
|
||||
got, err := ListURLWithQuery(tt.args.listURL, tt.args.options, tt.args.advancedIssueSearchSyntax)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("listURLWithQuery() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue