diff --git a/api/queries_issue.go b/api/queries_issue.go index 8826f2e7c..4c4c85b72 100644 --- a/api/queries_issue.go +++ b/api/queries_issue.go @@ -10,6 +10,7 @@ import ( "github.com/cli/cli/internal/ghrepo" "github.com/shurcooL/githubv4" + "github.com/shurcooL/graphql" ) type IssuesPayload struct { @@ -124,37 +125,30 @@ func IssueCreate(client *Client, repo *Repository, params map[string]interface{} } func CommentCreate(client *Client, repoHost string, params map[string]string) (string, error) { - query := ` - mutation CommentCreate($input: AddCommentInput!) { - addComment(input: $input) { - commentEdge { - node { - url - } - } - } - }` - - variables := map[string]interface{}{ - "input": params, - } - - result := struct { + var mutation struct { AddComment struct { CommentEdge struct { Node struct { URL string } } - } - }{} + } `graphql:"addComment(input: $input)"` + } - err := client.GraphQL(repoHost, query, variables, &result) + variables := map[string]interface{}{ + "input": githubv4.AddCommentInput{ + Body: githubv4.String(params["body"]), + SubjectID: graphql.ID(params["subjectId"]), + }, + } + + gql := graphQLClient(client.http, repoHost) + err := gql.MutateNamed(context.Background(), "CommentCreate", &mutation, variables) if err != nil { return "", err } - return result.AddComment.CommentEdge.Node.URL, nil + return mutation.AddComment.CommentEdge.Node.URL, nil } func IssueStatus(client *Client, repo ghrepo.Interface, currentUsername string) (*IssuesPayload, error) { diff --git a/pkg/cmd/issue/comment/comment.go b/pkg/cmd/issue/comment/comment.go index 09e77a520..5e73531fc 100644 --- a/pkg/cmd/issue/comment/comment.go +++ b/pkg/cmd/issue/comment/comment.go @@ -33,9 +33,9 @@ type CommentOptions struct { } const ( - inline = iota - editor + editor = iota web + inline ) func NewCmdComment(f *cmdutil.Factory, runF func(*CommentOptions) error) *cobra.Command { @@ -55,29 +55,39 @@ func NewCmdComment(f *cmdutil.Factory, runF func(*CommentOptions) error) *cobra. Use: "comment { | }", Short: "Create a new issue comment", Example: heredoc.Doc(` - $ gh issue comment 22 --body "I found a bug. Nothing works" + $ gh issue comment 22 --body "I was able to reproduce this issue, lets fix it." `), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo opts.SelectorArg = args[0] - opts.Interactive = !(opts.Body != "" || webMode || editorMode) - if !opts.IO.CanPrompt() && opts.Interactive { - return &cmdutil.FlagError{Err: errors.New("--body, --editor, or --web required when not running interactively")} + inputFlags := 0 + if cmd.Flags().Changed("body") { + opts.InputType = inline + inputFlags++ + } + if webMode { + opts.InputType = web + inputFlags++ + } + if editorMode { + opts.InputType = editor + inputFlags++ } - if !opts.Interactive { - if opts.Body != "" && !webMode && !editorMode { - opts.InputType = inline - } else if opts.Body == "" && webMode && !editorMode { - opts.InputType = web - } else if opts.Body == "" && !webMode && editorMode { - opts.InputType = editor - } else { - return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of --body, --editor, or --web")} + if inputFlags == 0 { + if !opts.IO.CanPrompt() { + return &cmdutil.FlagError{Err: errors.New("--body or --web required when not running interactively")} } + opts.Interactive = true + } else if inputFlags == 1 { + if !opts.IO.CanPrompt() && opts.InputType == editor { + return &cmdutil.FlagError{Err: errors.New("--body or --web required when not running interactively")} + } + } else if inputFlags > 1 { + return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of --body, --editor, or --web")} } if runF != nil { @@ -120,30 +130,22 @@ func commentRun(opts *CommentOptions) error { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } return utils.OpenInBrowser(openURL) - case inline: - if opts.Body != "" { - break - } - body, err := inlineSurvey() - if err != nil { - return err - } - opts.Body = body case editor: editorCommand, err := cmdutil.DetermineEditor(opts.Config) if err != nil { return err } - var body string - if opts.Interactive { - body, err = editorSurvey(editorCommand) - } else { - body, err = opts.Edit(editorCommand) - } + body, err := opts.Edit(editorCommand) if err != nil { return err } opts.Body = body + if opts.Interactive { + cs := opts.IO.ColorScheme() + fmt.Fprint(opts.IO.Out, cs.GreenBold("?")) + fmt.Fprint(opts.IO.Out, cs.Bold(" Body ")) + fmt.Fprint(opts.IO.Out, cs.Cyan("\n")) + } } if opts.Interactive { @@ -160,14 +162,11 @@ func commentRun(opts *CommentOptions) error { "subjectId": issue.ID, "body": opts.Body, } - url, err := api.CommentCreate(apiClient, baseRepo.RepoHost(), params) if err != nil { return err } - - fmt.Fprint(opts.IO.Out, url) - + fmt.Fprintln(opts.IO.Out, url) return nil } @@ -175,35 +174,12 @@ func inputTypeSurvey() (int, error) { var inputType int inputTypeQuestion := &survey.Select{ Message: "Where do you want to draft your comment?", - Options: []string{"In line", "Editor", "Web"}, + Options: []string{"Editor", "Web"}, } err := prompt.SurveyAskOne(inputTypeQuestion, &inputType) return inputType, err } -func inlineSurvey() (string, error) { - var body string - bodyQuestion := &survey.Input{ - Message: "Body", - } - err := prompt.SurveyAskOne(bodyQuestion, &body) - return body, err -} - -func editorSurvey(editorCommand string) (string, error) { - var body string - bodyQuestion := &surveyext.GhEditor{ - BlankAllowed: false, - EditorCommand: editorCommand, - Editor: &survey.Editor{ - Message: "Body", - FileName: "*.md", - }, - } - err := prompt.SurveyAskOne(bodyQuestion, &body) - return body, err -} - func confirmSubmitSurvey() (bool, error) { var confirm bool submit := &survey.Confirm{ diff --git a/pkg/cmd/issue/comment/comment_test.go b/pkg/cmd/issue/comment/comment_test.go index e601ffbb8..cbca6eaab 100644 --- a/pkg/cmd/issue/comment/comment_test.go +++ b/pkg/cmd/issue/comment/comment_test.go @@ -182,19 +182,9 @@ func Test_commentRun(t *testing.T) { Interactive: true, InputType: 0, Body: "", + Edit: func(string) (string, error) { return "comment body", nil }, }, - stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456", - }, - { - name: "interactive inline", - testInputType: inline, - input: &CommentOptions{ - SelectorArg: "123", - Interactive: true, - InputType: 0, - Body: "", - }, - stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456", + stdout: "? Body \nhttps://github.com/OWNER/REPO/issues/123#issuecomment-456\n", }, { name: "non-interactive web", @@ -217,7 +207,7 @@ func Test_commentRun(t *testing.T) { Body: "", Edit: func(string) (string, error) { return "comment body", nil }, }, - stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456", + stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n", }, { name: "non-interactive inline", @@ -228,7 +218,7 @@ func Test_commentRun(t *testing.T) { InputType: inline, Body: "comment body", }, - stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456", + stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n", }, } for _, tt := range tests { @@ -260,9 +250,7 @@ func Test_commentRun(t *testing.T) { defer teardown() // Input type select as.StubOne(tt.testInputType) - if tt.testInputType != web { - // Comment body - as.StubOne("comment body") + if tt.testInputType == editor { // Confirm submit as.StubOne(true) } diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 972caab3d..6b3626f13 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -9,15 +9,16 @@ import ( ) var ( - magenta = ansi.ColorFunc("magenta") - cyan = ansi.ColorFunc("cyan") - red = ansi.ColorFunc("red") - yellow = ansi.ColorFunc("yellow") - blue = ansi.ColorFunc("blue") - green = ansi.ColorFunc("green") - gray = ansi.ColorFunc("black+h") - bold = ansi.ColorFunc("default+b") - cyanBold = ansi.ColorFunc("cyan+b") + magenta = ansi.ColorFunc("magenta") + cyan = ansi.ColorFunc("cyan") + red = ansi.ColorFunc("red") + yellow = ansi.ColorFunc("yellow") + blue = ansi.ColorFunc("blue") + green = ansi.ColorFunc("green") + gray = ansi.ColorFunc("black+h") + bold = ansi.ColorFunc("default+b") + cyanBold = ansi.ColorFunc("cyan+b") + greenBold = ansi.ColorFunc("green+b") gray256 = func(t string) string { return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[m", 38, 242, t) @@ -84,6 +85,13 @@ func (c *ColorScheme) Green(t string) string { return green(t) } +func (c *ColorScheme) GreenBold(t string) string { + if !c.enabled { + return t + } + return greenBold(t) +} + func (c *ColorScheme) Gray(t string) string { if !c.enabled { return t