Extend @me replacing behavior to issue list

This commit is contained in:
Mislav Marohnić 2021-01-22 21:53:50 +01:00
parent 6ad0c57a31
commit 28c2d042e7
6 changed files with 104 additions and 30 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/cli/cli/api"
"github.com/cli/cli/internal/config"
"github.com/cli/cli/internal/ghrepo"
"github.com/cli/cli/pkg/cmd/pr/shared"
prShared "github.com/cli/cli/pkg/cmd/pr/shared"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/iostreams"
@ -115,9 +116,15 @@ func createRun(opts *CreateOptions) (err error) {
milestones = []string{opts.Milestone}
}
meReplacer := shared.NewMeReplacer(apiClient, baseRepo.RepoHost())
assignees, err := meReplacer.ReplaceSlice(opts.Assignees)
if err != nil {
return err
}
tb := prShared.IssueMetadataState{
Type: prShared.IssueMetadata,
Assignees: opts.Assignees,
Assignees: assignees,
Labels: opts.Labels,
Projects: opts.Projects,
Milestones: milestones,
@ -133,11 +140,6 @@ func createRun(opts *CreateOptions) (err error) {
}
}
opts.Assignees, err = prShared.ReplaceAtMeLogin(opts.Assignees, apiClient, baseRepo)
if err != nil {
return err
}
if opts.WebMode {
openURL := ghrepo.GenerateRepoURL(baseRepo, "issues/new")
if opts.Title != "" || opts.Body != "" || len(opts.Assignees) > 0 {

View file

@ -9,6 +9,7 @@ import (
"github.com/cli/cli/internal/config"
"github.com/cli/cli/internal/ghrepo"
issueShared "github.com/cli/cli/pkg/cmd/issue/shared"
"github.com/cli/cli/pkg/cmd/pr/shared"
prShared "github.com/cli/cli/pkg/cmd/pr/shared"
"github.com/cli/cli/pkg/cmdutil"
"github.com/cli/cli/pkg/iostreams"
@ -91,15 +92,29 @@ func listRun(opts *ListOptions) error {
isTerminal := opts.IO.IsStdoutTTY()
meReplacer := shared.NewMeReplacer(apiClient, baseRepo.RepoHost())
filterAssignee, err := meReplacer.Replace(opts.Assignee)
if err != nil {
return err
}
filterAuthor, err := meReplacer.Replace(opts.Author)
if err != nil {
return err
}
filterMention, err := meReplacer.Replace(opts.Mention)
if err != nil {
return err
}
if opts.WebMode {
issueListURL := ghrepo.GenerateRepoURL(baseRepo, "issues")
openURL, err := prShared.ListURLWithQuery(issueListURL, prShared.FilterOptions{
Entity: "issue",
State: opts.State,
Assignee: opts.Assignee,
Assignee: filterAssignee,
Labels: opts.Labels,
Author: opts.Author,
Mention: opts.Mention,
Author: filterAuthor,
Mention: filterMention,
Milestone: opts.Milestone,
})
if err != nil {
@ -111,7 +126,7 @@ func listRun(opts *ListOptions) error {
return utils.OpenInBrowser(openURL)
}
listResult, err := api.IssueList(apiClient, baseRepo, opts.State, opts.Labels, opts.Assignee, opts.LimitResults, opts.Author, opts.Mention, opts.Milestone)
listResult, err := api.IssueList(apiClient, baseRepo, opts.State, opts.Labels, filterAssignee, opts.LimitResults, filterAuthor, filterMention, opts.Milestone)
if err != nil {
return err
}

View file

@ -147,6 +147,31 @@ No issues match your search in OWNER/REPO
`, output.String())
}
func TestIssueList_atMe(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data": {"viewer": {"login": "monalisa"} } }`))
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "monalisa", params["assignee"].(string))
assert.Equal(t, "monalisa", params["author"].(string))
assert.Equal(t, "monalisa", params["mention"].(string))
}))
_, err := runCommand(http, true, "-a @me -A @me --mention @me")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
}
func TestIssueList_withInvalidLimitFlag(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)

View file

@ -386,10 +386,16 @@ func NewIssueState(ctx CreateContext, opts CreateOptions) (*shared.IssueMetadata
milestoneTitles = []string{opts.Milestone}
}
meReplacer := shared.NewMeReplacer(ctx.Client, ctx.BaseRepo.RepoHost())
assignees, err := meReplacer.ReplaceSlice(opts.Assignees)
if err != nil {
return nil, err
}
state := &shared.IssueMetadataState{
Type: shared.PRMetadata,
Reviewers: opts.Reviewers,
Assignees: opts.Assignees,
Assignees: assignees,
Labels: opts.Labels,
Projects: opts.Projects,
Milestones: milestoneTitles,

View file

@ -81,11 +81,6 @@ func AddMetadataToIssueParams(client *api.Client, baseRepo ghrepo.Interface, par
return nil
}
var err error
if tb.Assignees, err = ReplaceAtMeLogin(tb.Assignees, client, baseRepo); err != nil {
return err
}
if err := fillMetadata(client, baseRepo, tb); err != nil {
return err
}
@ -196,17 +191,47 @@ func quoteValueForQuery(v string) string {
return v
}
// ReplaceAtMeLogin iterates over the list of specified login names, replacing
// any "@me" mentions with the current user LoginName.
func ReplaceAtMeLogin(logins []string, client *api.Client, repo ghrepo.Interface) ([]string, error) {
for i, u := range logins {
if strings.EqualFold(u, "@me") {
login, err := api.CurrentLoginName(client, repo.RepoHost())
if err != nil {
return logins, fmt.Errorf("@me resolve: failed obtaining user id: %w", err)
}
logins[i] = login
// MeReplacer resolves usages of `@me` to the handle of the currently logged in user.
type MeReplacer struct {
apiClient *api.Client
hostname string
login string
}
func NewMeReplacer(apiClient *api.Client, hostname string) *MeReplacer {
return &MeReplacer{
apiClient: apiClient,
hostname: hostname,
}
}
func (r *MeReplacer) currentLogin() (string, error) {
if r.login != "" {
return r.login, nil
}
login, err := api.CurrentLoginName(r.apiClient, r.hostname)
if err != nil {
return "", fmt.Errorf("failed resolving `@me` to your user handle: %w", err)
}
r.login = login
return login, nil
}
func (r *MeReplacer) Replace(handle string) (string, error) {
if handle == "@me" {
return r.currentLogin()
}
return handle, nil
}
func (r *MeReplacer) ReplaceSlice(handles []string) ([]string, error) {
res := make([]string, len(handles))
for i, h := range handles {
var err error
res[i], err = r.Replace(h)
if err != nil {
return nil, err
}
}
return logins, nil
return res, nil
}

View file

@ -78,7 +78,7 @@ func Test_listURLWithQuery(t *testing.T) {
}
}
func TestReplaceAtMeLogin(t *testing.T) {
func TestMeReplacer_Replace(t *testing.T) {
rtSuccess := &httpmock.Registry{}
rtSuccess.Register(
httpmock.GraphQL(`query UserCurrent\b`),
@ -129,13 +129,14 @@ func TestReplaceAtMeLogin(t *testing.T) {
logins: []string{"some", "@me", "other"},
},
verify: rtFailure.Verify,
want: []string{"some", "@me", "other"},
want: []string(nil),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ReplaceAtMeLogin(tt.args.logins, tt.args.client, tt.args.repo)
me := NewMeReplacer(tt.args.client, tt.args.repo.RepoHost())
got, err := me.ReplaceSlice(tt.args.logins)
if (err != nil) != tt.wantErr {
t.Errorf("ReplaceAtMeLogin() error = %v, wantErr %v", err, tt.wantErr)
return