Skip to content

Commit

Permalink
refactor issue view package
Browse files Browse the repository at this point in the history
Enhancement to help work on profclems#322
  • Loading branch information
profclems committed Jan 16, 2021
1 parent 136432e commit 5a1db67
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 102 deletions.
3 changes: 2 additions & 1 deletion commands/issue/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ func NewCmdIssue(f *cmdutils.Factory) *cobra.Command {
Annotations: map[string]string{
"help:arguments": heredoc.Doc(`
An issue can be supplied as argument in any of the following formats:
- by number, e.g. "123".
- by number, e.g. "123"
- by URL, e.g. "https://gitlab.com/NAMESPACE/REPO/-/issues/123"
`),
},
}
Expand Down
194 changes: 100 additions & 94 deletions commands/issue/view/issue_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package view

import (
"fmt"
"io"
"strings"

"github.com/profclems/glab/commands/issue/issueutils"

"github.com/profclems/glab/commands/cmdutils"
"github.com/profclems/glab/internal/config"
"github.com/profclems/glab/internal/utils"
"github.com/profclems/glab/pkg/api"

Expand All @@ -17,20 +15,29 @@ import (
"github.com/xanzy/go-gitlab"
)

var (
showSystemLogs bool
showComments bool
limit int
pageNumber int
cfg config.Config
glamourStyle string
notes []*gitlab.Note
)
type ViewOpts struct {
ShowComments bool
ShowSystemLogs bool
OpenInBrowser bool
Web bool

CommentPageNumber int
CommentLimit int

Notes []*gitlab.Note
Issue *gitlab.Issue
GlamourStyle string

IO *utils.IOStreams
}

func NewCmdView(f *cmdutils.Factory) *cobra.Command {
opts := &ViewOpts{
IO: f.IO,
}
var issueViewCmd = &cobra.Command{
Use: "view <id>",
Short: `Display the title, body, and other information about an issue.`,
Short: `Display the title, body, and other information abopts.IO.StdErr an opts.Issue.`,
Long: ``,
Aliases: []string{"show"},
Example: heredoc.Doc(`
Expand All @@ -43,43 +50,42 @@ func NewCmdView(f *cmdutils.Factory) *cobra.Command {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

var err error
out := f.IO.StdOut

apiClient, err := f.HttpClient()
if err != nil {
return err
}
cfg, _ = f.Config()
cfg, _ := f.Config()

issue, baseRepo, err := issueutils.IssueFromArg(apiClient, f.BaseRepo, args[0])
if err != nil {
return err
}

opts.Issue = issue

//open in browser if --web flag is specified
if isWeb, _ := cmd.Flags().GetBool("web"); isWeb {
if opts.Web {
if f.IO.IsaTTY && f.IO.IsErrTTY {
fmt.Fprintf(out, "Opening %s in your browser.\n", utils.DisplayURL(issue.WebURL))
fmt.Fprintf(opts.IO.StdErr, "Opening %s in your browser.\n", utils.DisplayURL(opts.Issue.WebURL))
}

browser, _ := cfg.Get(baseRepo.RepoHost(), "browser")
return utils.OpenInBrowser(issue.WebURL, browser)
return utils.OpenInBrowser(opts.Issue.WebURL, browser)
}

glamourStyle, _ = cfg.Get(baseRepo.RepoHost(), "glamour_style")
opts.GlamourStyle, _ = cfg.Get(baseRepo.RepoHost(), "glamour_style")

if showComments {
if opts.ShowComments {
l := &gitlab.ListIssueNotesOptions{
Sort: gitlab.String("asc"),
}
if pageNumber != 0 {
l.Page = pageNumber
if opts.CommentPageNumber != 0 {
l.Page = opts.CommentPageNumber
}
if limit != 0 {
l.PerPage = limit
if opts.CommentLimit != 0 {
l.PerPage = opts.CommentLimit
}
notes, err = api.ListIssueNotes(apiClient, baseRepo.FullName(), issue.IID, l)
opts.Notes, err = api.ListIssueNotes(apiClient, baseRepo.FullName(), opts.Issue.IID, l)
if err != nil {
return err
}
Expand All @@ -91,134 +97,134 @@ func NewCmdView(f *cmdutils.Factory) *cobra.Command {
}
defer f.IO.StopPager()
if f.IO.IsErrTTY && f.IO.IsaTTY {
return printTTYIssuePreview(f.IO.StdOut, issue)
return printTTYIssuePreview(opts)
}
return printRawIssuePreview(f.IO.StdOut, issue)
return printRawIssuePreview(opts)
},
}

issueViewCmd.Flags().BoolVarP(&showComments, "comments", "c", false, "Show mr comments and activities")
issueViewCmd.Flags().BoolVarP(&showSystemLogs, "system-logs", "s", false, "Show system activities / logs")
issueViewCmd.Flags().BoolP("web", "w", false, "Open mr in a browser. Uses default browser or browser specified in BROWSER variable")
issueViewCmd.Flags().IntVarP(&pageNumber, "page", "p", 1, "Page number")
issueViewCmd.Flags().IntVarP(&limit, "per-page", "P", 20, "Number of items to list per page")
issueViewCmd.Flags().BoolVarP(&opts.ShowComments, "comments", "c", false, "Show mr comments and activities")
issueViewCmd.Flags().BoolVarP(&opts.ShowSystemLogs, "system-logs", "s", false, "Show system activities / logs")
issueViewCmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open mr in a browser. Uses default browser or browser specified in BROWSER variable")
issueViewCmd.Flags().IntVarP(&opts.CommentPageNumber, "page", "p", 1, "Page number")
issueViewCmd.Flags().IntVarP(&opts.CommentLimit, "per-page", "P", 20, "Number of items to list per page")

return issueViewCmd
}

func labelsList(issue *gitlab.Issue) string {
func labelsList(opts *ViewOpts) string {
var labels string
for _, l := range issue.Labels {
for _, l := range opts.Issue.Labels {
labels += " " + l + ","
}
return strings.Trim(labels, ", ")
}

func assigneesList(issue *gitlab.Issue) string {
func assigneesList(opts *ViewOpts) string {
var assignees string
for _, a := range issue.Assignees {
for _, a := range opts.Issue.Assignees {
assignees += " " + a.Username + ","
}
return strings.Trim(assignees, ", ")
}

func issueState(issue *gitlab.Issue) (state string) {
if issue.State == "opened" {
func issueState(opts *ViewOpts) (state string) {
if opts.Issue.State == "opened" {
state = utils.Green("open")
} else if issue.State == "locked" {
state = utils.Blue(issue.State)
} else if opts.Issue.State == "locked" {
state = utils.Blue(opts.Issue.State)
} else {
state = utils.Red(issue.State)
state = utils.Red(opts.Issue.State)
}

return
}

func printTTYIssuePreview(out io.Writer, issue *gitlab.Issue) error {
issueTimeAgo := utils.TimeToPrettyTimeAgo(*issue.CreatedAt)
func printTTYIssuePreview(opts *ViewOpts) error {
issueTimeAgo := utils.TimeToPrettyTimeAgo(*opts.Issue.CreatedAt)
// Header
fmt.Fprint(out, issueState(issue))
fmt.Fprintf(out, utils.Gray(" • opened by %s %s\n"), issue.Author.Username, issueTimeAgo)
fmt.Fprint(out, issue.Title)
fmt.Fprintf(out, utils.Gray(" #%d"), issue.IID)
fmt.Fprintln(out)
fmt.Fprint(opts.IO.StdOut, issueState(opts))
fmt.Fprintf(opts.IO.StdOut, utils.Gray(" • opened by %s %s\n"), opts.Issue.Author.Username, issueTimeAgo)
fmt.Fprint(opts.IO.StdOut, utils.Bold(opts.Issue.Title))
fmt.Fprintf(opts.IO.StdOut, utils.Gray(" #%d"), opts.Issue.IID)
fmt.Fprintln(opts.IO.StdOut)

// Description
if issue.Description != "" {
issue.Description, _ = utils.RenderMarkdown(issue.Description, glamourStyle)
fmt.Fprintln(out, issue.Description)
if opts.Issue.Description != "" {
opts.Issue.Description, _ = utils.RenderMarkdown(opts.Issue.Description, opts.GlamourStyle)
fmt.Fprintln(opts.IO.StdOut, opts.Issue.Description)
}

fmt.Fprintf(out, utils.Gray("\n%d upvotes • %d downvotes • %d comments\n"), issue.Upvotes, issue.Downvotes, issue.UserNotesCount)
fmt.Fprintf(opts.IO.StdOut, utils.Gray("\n%d upvotes • %d downvotes • %d comments\n"), opts.Issue.Upvotes, opts.Issue.Downvotes, opts.Issue.UserNotesCount)

// Meta information
if labels := labelsList(issue); labels != "" {
fmt.Fprint(out, utils.Bold("Labels: "))
fmt.Fprintln(out, labels)
if labels := labelsList(opts); labels != "" {
fmt.Fprint(opts.IO.StdOut, utils.Bold("Labels: "))
fmt.Fprintln(opts.IO.StdOut, labels)
}
if assignees := assigneesList(issue); assignees != "" {
fmt.Fprint(out, utils.Bold("Assignees: "))
fmt.Fprintln(out, assignees)
if assignees := assigneesList(opts); assignees != "" {
fmt.Fprint(opts.IO.StdOut, utils.Bold("Assignees: "))
fmt.Fprintln(opts.IO.StdOut, assignees)
}
if issue.Milestone != nil {
fmt.Fprint(out, utils.Bold("Milestone: "))
fmt.Fprintln(out, issue.Milestone.Title)
if opts.Issue.Milestone != nil {
fmt.Fprint(opts.IO.StdOut, utils.Bold("Milestone: "))
fmt.Fprintln(opts.IO.StdOut, opts.Issue.Milestone.Title)
}
if issue.State == "closed" {
fmt.Fprintf(out, "Closed By: %s %s\n", issue.ClosedBy.Username, issueTimeAgo)
if opts.Issue.State == "closed" {
fmt.Fprintf(opts.IO.StdOut, "Closed By: %s %s\n", opts.Issue.ClosedBy.Username, issueTimeAgo)
}

// Comments
if showComments {
fmt.Fprintln(out, heredoc.Doc(`
if opts.ShowComments {
fmt.Fprintln(opts.IO.StdOut, heredoc.Doc(`
--------------------------------------------
Comments / Notes
--------------------------------------------
`))
if len(notes) > 0 {
for _, note := range notes {
if note.System && !showSystemLogs {
if len(opts.Notes) > 0 {
for _, note := range opts.Notes {
if note.System && !opts.ShowSystemLogs {
continue
}
createdAt := utils.TimeToPrettyTimeAgo(*note.CreatedAt)
fmt.Fprint(out, note.Author.Username)
fmt.Fprint(opts.IO.StdOut, note.Author.Username)
if note.System {
fmt.Fprintf(out, " %s ", note.Body)
fmt.Fprintln(out, utils.Gray(createdAt))
fmt.Fprintf(opts.IO.StdOut, " %s ", note.Body)
fmt.Fprintln(opts.IO.StdOut, utils.Gray(createdAt))
} else {
body, _ := utils.RenderMarkdown(note.Body, glamourStyle)
fmt.Fprint(out, " commented ")
fmt.Fprintf(out, utils.Gray("%s\n"), createdAt)
fmt.Fprintln(out, utils.Indent(body, " "))
body, _ := utils.RenderMarkdown(note.Body, opts.GlamourStyle)
fmt.Fprint(opts.IO.StdOut, " commented ")
fmt.Fprintf(opts.IO.StdOut, utils.Gray("%s\n"), createdAt)
fmt.Fprintln(opts.IO.StdOut, utils.Indent(body, " "))
}
fmt.Fprintln(out)
fmt.Fprintln(opts.IO.StdOut)
}
} else {
fmt.Fprintln(out, "There are no comments on this issue")
fmt.Fprintln(opts.IO.StdOut, "There are no comments on this issue")
}
}

fmt.Fprintln(out)
fmt.Fprintf(out, utils.Gray("View this issue on GitLab: %s\n"), issue.WebURL)
fmt.Fprintln(opts.IO.StdOut)
fmt.Fprintf(opts.IO.StdOut, utils.Gray("View this issue on GitLab: %s\n"), opts.Issue.WebURL)

return nil
}

func printRawIssuePreview(out io.Writer, issue *gitlab.Issue) error {
assignees := assigneesList(issue)
labels := labelsList(issue)

fmt.Fprintf(out, "title:\t%s\n", issue.Title)
fmt.Fprintf(out, "state:\t%s\n", issueState(issue))
fmt.Fprintf(out, "author:\t%s\n", issue.Author.Username)
fmt.Fprintf(out, "labels:\t%s\n", labels)
fmt.Fprintf(out, "comments:\t%d\n", issue.UserNotesCount)
fmt.Fprintf(out, "assignees:\t%s\n", assignees)
if issue.Milestone != nil {
fmt.Fprintf(out, "milestone:\t%s\n", issue.Milestone.Title)
func printRawIssuePreview(opts *ViewOpts) error {
assignees := assigneesList(opts)
labels := labelsList(opts)

fmt.Fprintf(opts.IO.StdOut, "title:\t%s\n", opts.Issue.Title)
fmt.Fprintf(opts.IO.StdOut, "state:\t%s\n", issueState(opts))
fmt.Fprintf(opts.IO.StdOut, "author:\t%s\n", opts.Issue.Author.Username)
fmt.Fprintf(opts.IO.StdOut, "labels:\t%s\n", labels)
fmt.Fprintf(opts.IO.StdOut, "comments:\t%d\n", opts.Issue.UserNotesCount)
fmt.Fprintf(opts.IO.StdOut, "assignees:\t%s\n", assignees)
if opts.Issue.Milestone != nil {
fmt.Fprintf(opts.IO.StdOut, "milestone:\t%s\n", opts.Issue.Milestone.Title)
}

fmt.Fprintln(out, "--")
fmt.Fprintln(out, issue.Description)
fmt.Fprintln(opts.IO.StdOut, "--")
fmt.Fprintln(opts.IO.StdOut, opts.Issue.Description)
return nil
}
11 changes: 4 additions & 7 deletions commands/issue/view/issue_view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,14 @@ func TestNewCmdView_web_numberArg(t *testing.T) {
return
}

out := stripansi.Strip(stdout.String())
outErr := stripansi.Strip(stderr.String())
stdout.Reset()
stderr.Reset()

assert.Contains(t, out, "Opening gitlab.com/glab-cli/test/-/issues/225 in your browser.")
assert.Equal(t, "", outErr)
assert.Contains(t, stderr.String(), "Opening gitlab.com/glab-cli/test/-/issues/225 in your browser.")
assert.Equal(t, "", stdout.String())

if seenCmd == nil {
t.Log("expected a command to run")
}
stdout.Reset()
stderr.Reset()
}

func TestNewCmdView(t *testing.T) {
Expand Down

0 comments on commit 5a1db67

Please sign in to comment.