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"
|
scheduleCmd "gitlab.com/gitlab-org/cli/commands/schedule"
|
||||||
snippetCmd "gitlab.com/gitlab-org/cli/commands/snippet"
|
snippetCmd "gitlab.com/gitlab-org/cli/commands/snippet"
|
||||||
sshCmd "gitlab.com/gitlab-org/cli/commands/ssh-key"
|
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"
|
updateCmd "gitlab.com/gitlab-org/cli/commands/update"
|
||||||
userCmd "gitlab.com/gitlab-org/cli/commands/user"
|
userCmd "gitlab.com/gitlab-org/cli/commands/user"
|
||||||
variableCmd "gitlab.com/gitlab-org/cli/commands/variable"
|
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(scheduleCmd.NewCmdSchedule(f))
|
||||||
rootCmd.AddCommand(snippetCmd.NewCmdSnippet(f))
|
rootCmd.AddCommand(snippetCmd.NewCmdSnippet(f))
|
||||||
rootCmd.AddCommand(askCmd.NewCmd(f))
|
rootCmd.AddCommand(askCmd.NewCmd(f))
|
||||||
|
rootCmd.AddCommand(todoCmd.NewCmdTodo(f))
|
||||||
|
|
||||||
rootCmd.Flags().BoolP("version", "v", false, "show glab version information")
|
rootCmd.Flags().BoolP("version", "v", false, "show glab version information")
|
||||||
return rootCmd
|
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 {
|
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
|
emptyMessage := opts.EmptyMessage
|
||||||
|
|
Loading…
Reference in New Issue