Skip to content

Commit 02ed5a9

Browse files
authored
Merge pull request cli#4316 from SiarheiFedartsou/sf-pulls-draft
Add `--draft` and `--non-draft` filters to `gh pr list`
2 parents bb86145 + d14715f commit 02ed5a9

File tree

6 files changed

+130
-26
lines changed

6 files changed

+130
-26
lines changed

pkg/cmd/pr/list/http.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@ import (
1010
"github.com/cli/cli/v2/pkg/githubsearch"
1111
)
1212

13+
func shouldUseSearch(filters prShared.FilterOptions) bool {
14+
return filters.Draft != "" || filters.Author != "" || filters.Assignee != "" || filters.Search != "" || len(filters.Labels) > 0
15+
}
16+
1317
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 {
18+
if shouldUseSearch(filters) {
1519
return searchPullRequests(httpClient, repo, filters, limit)
1620
}
1721

@@ -177,6 +181,10 @@ func searchPullRequests(httpClient *http.Client, repo ghrepo.Interface, filters
177181
q.SetBaseBranch(filters.BaseBranch)
178182
}
179183

184+
if filters.Draft != "" {
185+
q.SetDraft(filters.Draft)
186+
}
187+
180188
pageLimit := min(limit, 100)
181189
variables := map[string]interface{}{
182190
"q": q.String(),

pkg/cmd/pr/list/list.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type ListOptions struct {
3737
Author string
3838
Assignee string
3939
Search string
40+
Draft string
4041
}
4142

4243
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
@@ -46,6 +47,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
4647
Browser: f.Browser,
4748
}
4849

50+
var draft bool
51+
4952
cmd := &cobra.Command{
5053
Use: "list",
5154
Short: "List and filter pull requests in this repository",
@@ -74,6 +77,10 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
7477
return &cmdutil.FlagError{Err: fmt.Errorf("invalid value for --limit: %v", opts.LimitResults)}
7578
}
7679

80+
if cmd.Flags().Changed("draft") {
81+
opts.Draft = strconv.FormatBool(draft)
82+
}
83+
7784
if runF != nil {
7885
return runF(opts)
7986
}
@@ -92,6 +99,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
9299
cmd.Flags().StringVarP(&opts.Author, "author", "A", "", "Filter by author")
93100
cmd.Flags().StringVarP(&opts.Assignee, "assignee", "a", "", "Filter by assignee")
94101
cmd.Flags().StringVarP(&opts.Search, "search", "S", "", "Search pull requests with `query`")
102+
cmd.Flags().BoolVarP(&draft, "draft", "d", false, "Filter by draft state")
103+
95104
cmdutil.AddJSONFlags(cmd, &opts.Exporter, api.PullRequestFields)
96105

97106
return cmd
@@ -132,12 +141,12 @@ func listRun(opts *ListOptions) error {
132141
Labels: opts.Labels,
133142
BaseBranch: opts.BaseBranch,
134143
Search: opts.Search,
144+
Draft: opts.Draft,
135145
Fields: defaultFields,
136146
}
137147
if opts.Exporter != nil {
138148
filters.Fields = opts.Exporter.Fields()
139149
}
140-
141150
if opts.WebMode {
142151
prListURL := ghrepo.GenerateRepoURL(baseRepo, "pulls")
143152
openURL, err := shared.ListURLWithQuery(prListURL, filters)

pkg/cmd/pr/list/list_test.go

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -176,39 +176,89 @@ func TestPRList_filteringAssignee(t *testing.T) {
176176
}
177177
}
178178

179-
func TestPRList_filteringAssigneeLabels(t *testing.T) {
180-
http := initFakeHTTP()
181-
defer http.Verify(t)
179+
func TestPRList_filteringDraft(t *testing.T) {
180+
tests := []struct {
181+
name string
182+
cli string
183+
expectedQuery string
184+
}{
185+
{
186+
name: "draft",
187+
cli: "--draft",
188+
expectedQuery: `repo:OWNER/REPO is:pr is:open draft:true`,
189+
},
190+
{
191+
name: "non-draft",
192+
cli: "--draft=false",
193+
expectedQuery: `repo:OWNER/REPO is:pr is:open draft:false`,
194+
},
195+
}
182196

183-
_, err := runCommand(http, true, `-l one,two -a hubot`)
184-
if err == nil && err.Error() != "multiple labels with --assignee are not supported" {
185-
t.Fatal(err)
197+
for _, test := range tests {
198+
t.Run(test.name, func(t *testing.T) {
199+
http := initFakeHTTP()
200+
defer http.Verify(t)
201+
202+
http.Register(
203+
httpmock.GraphQL(`query PullRequestSearch\b`),
204+
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
205+
assert.Equal(t, test.expectedQuery, params["q"].(string))
206+
}))
207+
208+
_, err := runCommand(http, true, test.cli)
209+
if err != nil {
210+
t.Fatal(err)
211+
}
212+
})
186213
}
187214
}
188215

189216
func TestPRList_withInvalidLimitFlag(t *testing.T) {
190217
http := initFakeHTTP()
191218
defer http.Verify(t)
192-
193219
_, err := runCommand(http, true, `--limit=0`)
194-
if err == nil && err.Error() != "invalid limit: 0" {
195-
t.Errorf("error running command `issue list`: %v", err)
196-
}
220+
assert.EqualError(t, err, "invalid value for --limit: 0")
197221
}
198222

199223
func TestPRList_web(t *testing.T) {
200-
http := initFakeHTTP()
201-
defer http.Verify(t)
224+
tests := []struct {
225+
name string
226+
cli string
227+
expectedBrowserURL string
228+
}{
229+
{
230+
name: "filters",
231+
cli: "-a peter -l bug -l docs -L 10 -s merged -B trunk",
232+
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Amerged+assignee%3Apeter+label%3Abug+label%3Adocs+base%3Atrunk",
233+
},
234+
{
235+
name: "draft",
236+
cli: "--draft=true",
237+
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Aopen+draft%3Atrue",
238+
},
239+
{
240+
name: "non-draft",
241+
cli: "--draft=0",
242+
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Aopen+draft%3Afalse",
243+
},
244+
}
202245

203-
_, cmdTeardown := run.Stub()
204-
defer cmdTeardown(t)
246+
for _, test := range tests {
247+
t.Run(test.name, func(t *testing.T) {
248+
http := initFakeHTTP()
249+
defer http.Verify(t)
205250

206-
output, err := runCommand(http, true, "--web -a peter -l bug -l docs -L 10 -s merged -B trunk")
207-
if err != nil {
208-
t.Errorf("error running command `pr list` with `--web` flag: %v", err)
209-
}
251+
_, cmdTeardown := run.Stub()
252+
defer cmdTeardown(t)
210253

211-
assert.Equal(t, "", output.String())
212-
assert.Equal(t, "Opening github.com/OWNER/REPO/pulls in your browser.\n", output.Stderr())
213-
assert.Equal(t, "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Amerged+assignee%3Apeter+label%3Abug+label%3Adocs+base%3Atrunk", output.BrowsedURL)
254+
output, err := runCommand(http, true, "--web "+test.cli)
255+
if err != nil {
256+
t.Errorf("error running command `pr list` with `--web` flag: %v", err)
257+
}
258+
259+
assert.Equal(t, "", output.String())
260+
assert.Equal(t, "Opening github.com/OWNER/REPO/pulls in your browser.\n", output.Stderr())
261+
assert.Equal(t, test.expectedBrowserURL, output.BrowsedURL)
262+
})
263+
}
214264
}

pkg/cmd/pr/shared/params.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ type FilterOptions struct {
157157
Mention string
158158
Milestone string
159159
Search string
160-
161-
Fields []string
160+
Draft string
161+
Fields []string
162162
}
163163

164164
func (opts *FilterOptions) IsDefault() bool {
@@ -241,7 +241,9 @@ func SearchQueryBuild(options FilterOptions) string {
241241
if options.Search != "" {
242242
q.AddQuery(options.Search)
243243
}
244-
244+
if options.Draft != "" {
245+
q.SetDraft(options.Draft)
246+
}
245247
return q.String()
246248
}
247249

pkg/cmd/pr/shared/params_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func Test_listURLWithQuery(t *testing.T) {
1616
listURL string
1717
options FilterOptions
1818
}
19+
1920
tests := []struct {
2021
name string
2122
args args
@@ -34,6 +35,32 @@ func Test_listURLWithQuery(t *testing.T) {
3435
want: "https://example.com/path?a=b&q=is%3Aissue+is%3Aopen",
3536
wantErr: false,
3637
},
38+
{
39+
name: "draft",
40+
args: args{
41+
listURL: "https://example.com/path",
42+
options: FilterOptions{
43+
Entity: "pr",
44+
State: "open",
45+
Draft: "true",
46+
},
47+
},
48+
want: "https://example.com/path?q=is%3Apr+is%3Aopen+draft%3Atrue",
49+
wantErr: false,
50+
},
51+
{
52+
name: "non-draft",
53+
args: args{
54+
listURL: "https://example.com/path",
55+
options: FilterOptions{
56+
Entity: "pr",
57+
State: "open",
58+
Draft: "false",
59+
},
60+
},
61+
want: "https://example.com/path?q=is%3Apr+is%3Aopen+draft%3Afalse",
62+
wantErr: false,
63+
},
3764
{
3865
name: "all",
3966
args: args{

pkg/githubsearch/query.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ type Query struct {
5454
forkState string
5555
visibility string
5656
isArchived *bool
57+
draft string
5758
}
5859

5960
func (q *Query) InRepository(nameWithOwner string) {
@@ -139,6 +140,10 @@ func (q *Query) SetArchived(isArchived bool) {
139140
q.isArchived = &isArchived
140141
}
141142

143+
func (q *Query) SetDraft(draft string) {
144+
q.draft = draft
145+
}
146+
142147
func (q *Query) String() string {
143148
var qs string
144149

@@ -198,6 +203,9 @@ func (q *Query) String() string {
198203
if q.headBranch != "" {
199204
qs += fmt.Sprintf("head:%s ", quote(q.headBranch))
200205
}
206+
if q.draft != "" {
207+
qs += fmt.Sprintf("draft:%v ", q.draft)
208+
}
201209

202210
if q.sort != "" {
203211
qs += fmt.Sprintf("sort:%s ", q.sort)

0 commit comments

Comments
 (0)