Skip to content

Commit e12c35c

Browse files
committed
Add ability to pass milestone by number
1 parent 20ffa49 commit e12c35c

File tree

4 files changed

+86
-21
lines changed

4 files changed

+86
-21
lines changed

api/queries_issue.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/base64"
66
"fmt"
7+
"strconv"
78
"strings"
89
"time"
910

@@ -250,25 +251,24 @@ func IssueList(client *Client, repo ghrepo.Interface, state string, labels []str
250251
}
251252

252253
if milestoneString != "" {
253-
milestones, err := RepoMilestones(client, repo)
254-
if err != nil {
255-
return nil, err
256-
}
257-
258-
for i := range milestones {
259-
if strings.EqualFold(milestones[i].Title, milestoneString) {
260-
id, err := milestoneNodeIdToDatabaseId(milestones[i].ID)
261-
if err != nil {
262-
return nil, err
263-
}
264-
variables["milestone"] = id
265-
break
254+
var milestone *RepoMilestone
255+
if milestoneNumber, err := strconv.Atoi(milestoneString); err == nil {
256+
milestone, err = MilestoneByNumber(client, repo, milestoneNumber)
257+
if err != nil {
258+
return nil, err
259+
}
260+
} else {
261+
milestone, err = MilestoneByTitle(client, repo, milestoneString)
262+
if err != nil {
263+
return nil, err
266264
}
267265
}
268266

269-
if variables["milestone"] == nil {
270-
return nil, fmt.Errorf("no milestone found with title: %q", milestoneString)
267+
milestoneRESTID, err := milestoneNodeIdToDatabaseId(milestone.ID)
268+
if err != nil {
269+
return nil, err
271270
}
271+
variables["milestone"] = milestoneRESTID
272272
}
273273

274274
var response struct {

api/queries_repo.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,3 +804,43 @@ func RepoMilestones(client *Client, repo ghrepo.Interface) ([]RepoMilestone, err
804804

805805
return milestones, nil
806806
}
807+
808+
func MilestoneByTitle(client *Client, repo ghrepo.Interface, title string) (*RepoMilestone, error) {
809+
milestones, err := RepoMilestones(client, repo)
810+
if err != nil {
811+
return nil, err
812+
}
813+
814+
for i := range milestones {
815+
if strings.EqualFold(milestones[i].Title, title) {
816+
return &milestones[i], nil
817+
}
818+
}
819+
return nil, fmt.Errorf("no milestone found with title %q", title)
820+
}
821+
822+
func MilestoneByNumber(client *Client, repo ghrepo.Interface, number int) (*RepoMilestone, error) {
823+
var query struct {
824+
Repository struct {
825+
Milestone *RepoMilestone `graphql:"milestone(number: $number)"`
826+
} `graphql:"repository(owner: $owner, name: $name)"`
827+
}
828+
829+
variables := map[string]interface{}{
830+
"owner": githubv4.String(repo.RepoOwner()),
831+
"name": githubv4.String(repo.RepoName()),
832+
"number": githubv4.Int(number),
833+
}
834+
835+
gql := graphQLClient(client.http, repo.RepoHost())
836+
837+
err := gql.QueryNamed(context.Background(), "RepositoryMilestoneByNumber", &query, variables)
838+
if err != nil {
839+
return nil, err
840+
}
841+
if query.Repository.Milestone == nil {
842+
return nil, fmt.Errorf("no milestone found with number '%d'", number)
843+
}
844+
845+
return query.Repository.Milestone, nil
846+
}

command/issue.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func init() {
4646
issueListCmd.Flags().IntP("limit", "L", 30, "Maximum number of issues to fetch")
4747
issueListCmd.Flags().StringP("author", "A", "", "Filter by author")
4848
issueListCmd.Flags().String("mention", "", "Filter by mention")
49-
issueListCmd.Flags().StringP("milestone", "m", "", "Filter by milestone `name`")
49+
issueListCmd.Flags().StringP("milestone", "m", "", "Filter by milestone `number` or title")
5050

5151
issueCmd.AddCommand(issueViewCmd)
5252
issueViewCmd.Flags().BoolP("web", "w", false, "Open an issue in the browser")

command/issue_test.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,8 @@ func TestIssueList_web(t *testing.T) {
285285
func TestIssueList_milestoneNotFound(t *testing.T) {
286286
initBlankContext("", "OWNER/REPO", "master")
287287
http := initFakeHTTP()
288+
defer http.Verify(t)
288289
http.StubRepoResponse("OWNER", "REPO")
289-
http.Register(
290-
httpmock.GraphQL(`query IssueList\b`),
291-
httpmock.FileResponse("../test/fixtures/issueList.json"),
292-
)
293290
http.Register(
294291
httpmock.GraphQL(`query RepositoryMilestoneList\b`),
295292
httpmock.StringResponse(`
@@ -300,11 +297,39 @@ func TestIssueList_milestoneNotFound(t *testing.T) {
300297
`))
301298

302299
_, err := RunCommand("issue list --milestone NotFound")
303-
if err == nil || err.Error() != `no milestone found with title: "NotFound"` {
300+
if err == nil || err.Error() != `no milestone found with title "NotFound"` {
304301
t.Errorf("error running command `issue list`: %v", err)
305302
}
306303
}
307304

305+
func TestIssueList_milestoneByNumber(t *testing.T) {
306+
initBlankContext("", "OWNER/REPO", "master")
307+
http := initFakeHTTP()
308+
defer http.Verify(t)
309+
http.StubRepoResponse("OWNER", "REPO")
310+
http.Register(
311+
httpmock.GraphQL(`query RepositoryMilestoneByNumber\b`),
312+
httpmock.StringResponse(`
313+
{ "data": { "repository": { "milestone": {
314+
"id": "MDk6TWlsZXN0b25lMTIzNDU="
315+
} } } }
316+
`))
317+
http.Register(
318+
httpmock.GraphQL(`query IssueList\b`),
319+
httpmock.GraphQLQuery(`
320+
{ "data": { "repository": {
321+
"hasIssuesEnabled": true,
322+
"issues": { "nodes": [] }
323+
} } }`, func(_ string, params map[string]interface{}) {
324+
assert.Equal(t, "12345", params["milestone"].(string)) // Database ID for the Milestone (see #1462)
325+
}))
326+
327+
_, err := RunCommand("issue list --milestone 13")
328+
if err != nil {
329+
t.Fatalf("error running issue list: %v", err)
330+
}
331+
}
332+
308333
func TestIssueView_web(t *testing.T) {
309334
initBlankContext("", "OWNER/REPO", "master")
310335
http := initFakeHTTP()

0 commit comments

Comments
 (0)