mirror of https://gitlab.com/gitlab-org/cli.git
Added todos list command
Added a new command to list open todos https://gitlab.com/gitlab-org/cli/-/issues/1114
This commit is contained in:
parent
2de2d082c8
commit
5acdce7577
|
@ -0,0 +1,19 @@
|
|||
package api
|
||||
|
||||
import "github.com/xanzy/go-gitlab"
|
||||
|
||||
var ListTodos = func(client *gitlab.Client, opts *gitlab.ListTodosOptions) ([]*gitlab.Todo, *gitlab.Response, error) {
|
||||
if client == nil {
|
||||
client = apiClient.Lab()
|
||||
}
|
||||
|
||||
if opts.PerPage == 0 {
|
||||
opts.PerPage = DefaultListLimit
|
||||
}
|
||||
|
||||
todos, resp, err := client.Todos.ListTodos(opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return todos, resp, nil
|
||||
}
|
|
@ -26,6 +26,7 @@ import (
|
|||
scheduleCmd "gitlab.com/gitlab-org/cli/commands/schedule"
|
||||
snippetCmd "gitlab.com/gitlab-org/cli/commands/snippet"
|
||||
sshCmd "gitlab.com/gitlab-org/cli/commands/ssh-key"
|
||||
todoCmd "gitlab.com/gitlab-org/cli/commands/todo"
|
||||
updateCmd "gitlab.com/gitlab-org/cli/commands/update"
|
||||
userCmd "gitlab.com/gitlab-org/cli/commands/user"
|
||||
variableCmd "gitlab.com/gitlab-org/cli/commands/variable"
|
||||
|
@ -126,6 +127,7 @@ func NewCmdRoot(f *cmdutils.Factory, version, buildDate string) *cobra.Command {
|
|||
rootCmd.AddCommand(scheduleCmd.NewCmdSchedule(f))
|
||||
rootCmd.AddCommand(snippetCmd.NewCmdSnippet(f))
|
||||
rootCmd.AddCommand(askCmd.NewCmd(f))
|
||||
rootCmd.AddCommand(todoCmd.NewCmdTodo(f))
|
||||
|
||||
rootCmd.Flags().BoolP("version", "v", false, "show glab version information")
|
||||
return rootCmd
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitlab.com/gitlab-org/cli/api"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
||||
"gitlab.com/gitlab-org/cli/pkg/iostreams"
|
||||
"gitlab.com/gitlab-org/cli/pkg/tableprinter"
|
||||
"gitlab.com/gitlab-org/cli/pkg/utils"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
||||
"gitlab.com/gitlab-org/cli/commands/todo/todoutils"
|
||||
)
|
||||
|
||||
type ListOptions struct {
|
||||
State string
|
||||
|
||||
// Pagination
|
||||
Page int
|
||||
PerPage int
|
||||
|
||||
// display opts
|
||||
TitleQualifier string
|
||||
|
||||
IO *iostreams.IOStreams
|
||||
HTTPClient func() (*gitlab.Client, error)
|
||||
}
|
||||
|
||||
func DisplayAllTodos(streams *iostreams.IOStreams, todos []*gitlab.Todo) string {
|
||||
table := tableprinter.NewTablePrinter()
|
||||
table.SetIsTTY(streams.IsOutputTTY())
|
||||
for _, todo := range todos {
|
||||
table.AddCell(todoutils.TodoActionName(todo))
|
||||
table.AddCell(streams.Hyperlink(fmt.Sprintf("%s%s", todo.Project.PathWithNamespace, todo.Target.Reference), todo.TargetURL))
|
||||
table.AddCell(todo.Body)
|
||||
table.EndRow()
|
||||
}
|
||||
|
||||
return table.Render()
|
||||
}
|
||||
|
||||
func NewCmdList(f *cmdutils.Factory, runE func(opts *ListOptions) error) *cobra.Command {
|
||||
opts := &ListOptions{
|
||||
IO: f.IO,
|
||||
}
|
||||
|
||||
todoListCmd := &cobra.Command{
|
||||
Use: "list [flags]",
|
||||
Short: `List your todos`,
|
||||
Long: ``,
|
||||
Aliases: []string{"ls"},
|
||||
Example: heredoc.Doc(`
|
||||
glab todo list
|
||||
glab todo list --state done
|
||||
`),
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var err error
|
||||
|
||||
opts.HTTPClient = f.HttpClient
|
||||
|
||||
apiClient, err := opts.HTTPClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l := &gitlab.ListTodosOptions{}
|
||||
|
||||
if p, _ := cmd.Flags().GetInt("page"); p != 0 {
|
||||
opts.Page = p
|
||||
l.Page = p
|
||||
}
|
||||
|
||||
if p, _ := cmd.Flags().GetInt("per-page"); p != 0 {
|
||||
opts.PerPage = p
|
||||
l.PerPage = p
|
||||
}
|
||||
|
||||
if state, _ := cmd.Flags().GetString("state"); state != "" {
|
||||
opts.State = state
|
||||
l.State = gitlab.String(state)
|
||||
}
|
||||
|
||||
title := utils.NewListTitle(opts.TitleQualifier + " todo")
|
||||
todos, _, err := api.ListTodos(apiClient, l)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
title.Page = l.Page
|
||||
title.CurrentPageTotal = len(todos)
|
||||
|
||||
fmt.Fprintf(opts.IO.StdOut, "%s\n%s\n", title.Describe(), DisplayAllTodos(opts.IO, todos))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
todoListCmd.Flags().IntP("page", "p", 1, "Page number")
|
||||
todoListCmd.Flags().IntP("per-page", "P", 30, "Number of items to list per page")
|
||||
todoListCmd.Flags().StringP("state", "s", "pending", "State of todo")
|
||||
|
||||
return todoListCmd
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"gitlab.com/gitlab-org/cli/pkg/iostreams"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdtest"
|
||||
"gitlab.com/gitlab-org/cli/pkg/httpmock"
|
||||
"gitlab.com/gitlab-org/cli/test"
|
||||
)
|
||||
|
||||
func runCommand(rt http.RoundTripper) (*test.CmdOut, error) {
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
factory := cmdtest.InitFactory(ios, rt)
|
||||
|
||||
_, _ = factory.HttpClient()
|
||||
|
||||
cmd := NewCmdList(factory, nil)
|
||||
|
||||
_, err := cmd.ExecuteC()
|
||||
return &test.CmdOut{
|
||||
OutBuf: stdout,
|
||||
ErrBuf: stderr,
|
||||
}, err
|
||||
}
|
||||
|
||||
func TestTodoList(t *testing.T) {
|
||||
fakeHTTP := &httpmock.Mocker{}
|
||||
defer fakeHTTP.Verify(t)
|
||||
|
||||
fakeHTTP.RegisterResponder(http.MethodGet, "/api/v4/todos",
|
||||
httpmock.NewStringResponse(http.StatusOK, `
|
||||
[
|
||||
{
|
||||
"id": 102,
|
||||
"project": {
|
||||
"path_with_namespace": "gitlab-org/gitlab-foss"
|
||||
},
|
||||
"action_name": "marked",
|
||||
"target": {
|
||||
"reference": "!1"
|
||||
},
|
||||
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-foss/-/merge_requests/7"
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"project": {
|
||||
"path_with_namespace": "gitlab-org/gitlab-foss"
|
||||
},
|
||||
"action_name": "build_failed",
|
||||
"target": {
|
||||
"reference": "!1"
|
||||
},
|
||||
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-foss/-/merge_requests/7"
|
||||
}
|
||||
]
|
||||
`))
|
||||
|
||||
output, err := runCommand(fakeHTTP)
|
||||
if err != nil {
|
||||
t.Errorf("error running command `todo list`: %v", err)
|
||||
}
|
||||
|
||||
out := output.String()
|
||||
|
||||
assert.Equal(t, heredoc.Doc(`
|
||||
Showing 2 todos (Page 1)
|
||||
|
||||
Added todo gitlab-org/gitlab-foss!1
|
||||
Pipeline failed gitlab-org/gitlab-foss!1
|
||||
|
||||
`), out)
|
||||
assert.Empty(t, output.Stderr())
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package todo
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
||||
todoListCmd "gitlab.com/gitlab-org/cli/commands/todo/list"
|
||||
)
|
||||
|
||||
func NewCmdTodo(f *cmdutils.Factory) *cobra.Command {
|
||||
todoCmd := &cobra.Command{
|
||||
Use: "todo <command> [flags]",
|
||||
Short: `List todos`,
|
||||
Long: ``,
|
||||
}
|
||||
|
||||
todoCmd.AddCommand(todoListCmd.NewCmdList(f, nil))
|
||||
return todoCmd
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package todo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gitlab.com/gitlab-org/cli/commands/cmdutils"
|
||||
)
|
||||
|
||||
func TestNewCmdTodo(t *testing.T) {
|
||||
old := os.Stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
assert.Nil(t, NewCmdTodo(&cmdutils.Factory{}).Execute())
|
||||
|
||||
outC := make(chan string)
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
outC <- buf.String()
|
||||
}()
|
||||
|
||||
w.Close()
|
||||
os.Stdout = old
|
||||
out := <-outC
|
||||
|
||||
assert.Contains(t, out, " \"todo [command]")
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package todoutils
|
||||
|
||||
import (
|
||||
"github.com/xanzy/go-gitlab"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TodoActionName(todo *gitlab.Todo) string {
|
||||
switch todo.ActionName {
|
||||
case "approval_required":
|
||||
return "Approval required"
|
||||
case "build_failed":
|
||||
return "Pipeline failed"
|
||||
case "directly_addressed":
|
||||
return "Mentioned"
|
||||
case "marked":
|
||||
return "Added todo"
|
||||
case "merge_train_removed":
|
||||
return "Removed from merge train"
|
||||
case "review_requested":
|
||||
return "Review requested"
|
||||
case "review_submitted":
|
||||
return "Review submitted"
|
||||
default:
|
||||
return cases.Title(language.English, cases.NoLower).String(string(todo.ActionName))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
<!--
|
||||
This documentation is auto generated by a script.
|
||||
Please do not edit this file directly. Run `make gen-docs` instead.
|
||||
-->
|
||||
|
||||
# `glab todo help`
|
||||
|
||||
Help about any command
|
||||
|
||||
```plaintext
|
||||
glab todo help [command] [flags]
|
||||
```
|
||||
|
||||
## Options inherited from parent commands
|
||||
|
||||
```plaintext
|
||||
--help Show help for command
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
<!--
|
||||
This documentation is auto generated by a script.
|
||||
Please do not edit this file directly. Run `make gen-docs` instead.
|
||||
-->
|
||||
|
||||
# `glab todo`
|
||||
|
||||
List todos
|
||||
|
||||
## Options inherited from parent commands
|
||||
|
||||
```plaintext
|
||||
--help Show help for command
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
- [`list`](list.md)
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Code Review
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
<!--
|
||||
This documentation is auto generated by a script.
|
||||
Please do not edit this file directly. Run `make gen-docs` instead.
|
||||
-->
|
||||
|
||||
# `glab todo list`
|
||||
|
||||
List your todos
|
||||
|
||||
```plaintext
|
||||
glab todo list [flags]
|
||||
```
|
||||
|
||||
## Aliases
|
||||
|
||||
```plaintext
|
||||
ls
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```plaintext
|
||||
glab todo list
|
||||
glab todo list --state done
|
||||
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
```plaintext
|
||||
-p, --page int Page number (default 1)
|
||||
-P, --per-page int Number of items to list per page (default 30)
|
||||
-s, --state string State of todo (default "pending")
|
||||
```
|
||||
|
||||
## Options inherited from parent commands
|
||||
|
||||
```plaintext
|
||||
--help Show help for command
|
||||
```
|
|
@ -67,7 +67,11 @@ func (opts *ListTitleOptions) Describe() string {
|
|||
}
|
||||
|
||||
if opts.CurrentPageTotal > 0 {
|
||||
return fmt.Sprintf("Showing %s %s on %s %s\n", pageNumInfo, opts.Name, opts.RepoName, pageInfo)
|
||||
if opts.RepoName == "" {
|
||||
return fmt.Sprintf("Showing %s %s %s\n", pageNumInfo, opts.Name, pageInfo)
|
||||
} else {
|
||||
return fmt.Sprintf("Showing %s %s on %s %s\n", pageNumInfo, opts.Name, opts.RepoName, pageInfo)
|
||||
}
|
||||
}
|
||||
|
||||
emptyMessage := opts.EmptyMessage
|
||||
|
|
Loading…
Reference in New Issue