Add `--fields` to notification & milestone listings (#422)

Together with #415 this finally adds the field flag to all entity listings.
closes #342

### ⚠️ breaking changes ⚠️
This changes the column names of `tea milestones ls`:

```diff
 - TITLE  | OPEN/CLOSED ISSUES | DUEDATE
 + TITLE  | ITEMS | DUEDATE
```

Co-authored-by: Norwin <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/422
Reviewed-by: delvh <dev.lh@web.de>
Reviewed-by: 6543 <6543@obermui.de>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
Norwin 2022-09-14 03:08:18 +08:00 committed by 6543
parent bbb287e29e
commit 99e49991bb
5 changed files with 149 additions and 80 deletions

View File

@ -13,6 +13,10 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var fieldsFlag = flags.FieldsFlag(print.MilestoneFields, []string{
"title", "items", "duedate",
})
// CmdMilestonesList represents a sub command of milestones to list milestones // CmdMilestonesList represents a sub command of milestones to list milestones
var CmdMilestonesList = cli.Command{ var CmdMilestonesList = cli.Command{
Name: "list", Name: "list",
@ -22,6 +26,7 @@ var CmdMilestonesList = cli.Command{
ArgsUsage: " ", // command does not accept arguments ArgsUsage: " ", // command does not accept arguments
Action: RunMilestonesList, Action: RunMilestonesList,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
fieldsFlag,
&cli.StringFlag{ &cli.StringFlag{
Name: "state", Name: "state",
Usage: "Filter by milestone state (all|open|closed)", Usage: "Filter by milestone state (all|open|closed)",
@ -37,10 +42,18 @@ func RunMilestonesList(cmd *cli.Context) error {
ctx := context.InitCommand(cmd) ctx := context.InitCommand(cmd)
ctx.Ensure(context.CtxRequirement{RemoteRepo: true}) ctx.Ensure(context.CtxRequirement{RemoteRepo: true})
fields, err := fieldsFlag.GetValues(cmd)
if err != nil {
return err
}
state := gitea.StateOpen state := gitea.StateOpen
switch ctx.String("state") { switch ctx.String("state") {
case "all": case "all":
state = gitea.StateAll state = gitea.StateAll
if !cmd.IsSet("fields") { // add to default fields
fields = append(fields, "state")
}
case "closed": case "closed":
state = gitea.StateClosed state = gitea.StateClosed
} }
@ -55,6 +68,6 @@ func RunMilestonesList(cmd *cli.Context) error {
return err return err
} }
print.MilestonesList(milestones, ctx.Output, state) print.MilestonesList(milestones, ctx.Output, fields)
return nil return nil
} }

View File

@ -15,7 +15,11 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var notifTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"}, var notifyFieldsFlag = flags.FieldsFlag(print.NotificationFields, []string{
"id", "status", "index", "type", "state", "title",
})
var notifyTypeFlag = flags.NewCsvFlag("types", "subject types to filter by", []string{"t"},
[]string{"issue", "pull", "repository", "commit"}, nil) []string{"issue", "pull", "repository", "commit"}, nil)
// CmdNotificationsList represents a sub command of notifications to list notifications // CmdNotificationsList represents a sub command of notifications to list notifications
@ -26,7 +30,10 @@ var CmdNotificationsList = cli.Command{
Description: `List notifications`, Description: `List notifications`,
ArgsUsage: " ", // command does not accept arguments ArgsUsage: " ", // command does not accept arguments
Action: RunNotificationsList, Action: RunNotificationsList,
Flags: append([]cli.Flag{notifTypeFlag}, flags.NotificationFlags...), Flags: append([]cli.Flag{
notifyFieldsFlag,
notifyTypeFlag,
}, flags.NotificationFlags...),
} }
// RunNotificationsList list notifications // RunNotificationsList list notifications
@ -41,7 +48,7 @@ func RunNotificationsList(ctx *cli.Context) error {
} }
var types []gitea.NotifySubjectType var types []gitea.NotifySubjectType
typesStr, err := notifTypeFlag.GetValues(ctx) typesStr, err := notifyTypeFlag.GetValues(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -67,7 +74,17 @@ func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects [
listOpts.Page = 1 listOpts.Page = 1
} }
fields, err := notifyFieldsFlag.GetValues(cmd)
if err != nil {
return err
}
if all { if all {
// add repository to the default fields
if !cmd.IsSet("fields") {
fields = append(fields, "repository")
}
news, _, err = client.ListNotifications(gitea.ListNotificationOptions{ news, _, err = client.ListNotifications(gitea.ListNotificationOptions{
ListOptions: listOpts, ListOptions: listOpts,
Status: status, Status: status,
@ -85,6 +102,6 @@ func listNotifications(cmd *cli.Context, status []gitea.NotifyStatus, subjects [
log.Fatal(err) log.Fatal(err)
} }
print.NotificationsList(news, ctx.Output, all) print.NotificationsList(news, ctx.Output, fields)
return nil return nil
} }

View File

@ -28,7 +28,7 @@ var CmdNotificationsMarkRead = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)} filter = []string{string(gitea.NotifyStatusUnread)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusRead) return markNotificationAs(cmd, filter, gitea.NotifyStatusRead)
@ -49,7 +49,7 @@ var CmdNotificationsMarkUnread = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusRead)} filter = []string{string(gitea.NotifyStatusRead)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread) return markNotificationAs(cmd, filter, gitea.NotifyStatusUnread)
@ -70,7 +70,7 @@ var CmdNotificationsMarkPinned = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if !flags.NotificationStateFlag.IsSet() { if !cmd.IsSet(flags.NotificationStateFlag.Name) {
filter = []string{string(gitea.NotifyStatusUnread)} filter = []string{string(gitea.NotifyStatusUnread)}
} }
return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned) return markNotificationAs(cmd, filter, gitea.NotifyStatusPinned)

View File

@ -24,40 +24,65 @@ func MilestoneDetails(milestone *gitea.Milestone) {
} }
// MilestonesList prints a listing of milestones // MilestonesList prints a listing of milestones
func MilestonesList(miles []*gitea.Milestone, output string, state gitea.StateType) { func MilestonesList(news []*gitea.Milestone, output string, fields []string) {
headers := []string{ var printables = make([]printable, len(news))
"Title", for i, x := range news {
printables[i] = &printableMilestone{x}
} }
if state == gitea.StateAll { t := tableFromItems(fields, printables, isMachineReadable(output))
headers = append(headers, "State")
}
headers = append(headers,
"Open/Closed Issues",
"DueDate",
)
t := table{headers: headers}
for _, m := range miles {
var deadline = ""
if m.Deadline != nil && !m.Deadline.IsZero() {
deadline = FormatTime(*m.Deadline, isMachineReadable(output))
}
item := []string{
m.Title,
}
if state == gitea.StateAll {
item = append(item, string(m.State))
}
item = append(item,
fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues),
deadline,
)
t.addRowSlice(item)
}
t.sort(0, true) t.sort(0, true)
t.print(output) t.print(output)
} }
// MilestoneFields are all available fields to print with MilestonesList
var MilestoneFields = []string{
"title",
"state",
"items_open",
"items_closed",
"items",
"duedate",
"description",
"created",
"updated",
"closed",
"id",
}
type printableMilestone struct {
*gitea.Milestone
}
func (m printableMilestone) FormatField(field string, machineReadable bool) string {
switch field {
case "title":
return m.Title
case "state":
return string(m.State)
case "items_open":
return fmt.Sprintf("%d", m.OpenIssues)
case "items_closed":
return fmt.Sprintf("%d", m.ClosedIssues)
case "items":
return fmt.Sprintf("%d/%d", m.OpenIssues, m.ClosedIssues)
case "duedate":
if m.Deadline != nil && !m.Deadline.IsZero() {
return FormatTime(*m.Deadline, machineReadable)
}
case "id":
return fmt.Sprintf("%d", m.ID)
case "description":
return m.Description
case "created":
return FormatTime(m.Created, machineReadable)
case "updated":
if m.Updated != nil {
return FormatTime(*m.Updated, machineReadable)
}
case "closed":
if m.Closed != nil {
return FormatTime(*m.Closed, machineReadable)
}
}
return ""
}

View File

@ -12,26 +12,51 @@ import (
) )
// NotificationsList prints a listing of notification threads // NotificationsList prints a listing of notification threads
func NotificationsList(news []*gitea.NotificationThread, output string, showRepository bool) { func NotificationsList(news []*gitea.NotificationThread, output string, fields []string) {
headers := []string{ var printables = make([]printable, len(news))
"ID", for i, x := range news {
"Status", printables[i] = &printableNotification{x}
"Type",
"State",
"Index",
"Title",
}
if showRepository {
headers = append(headers, "Repository")
} }
t := tableFromItems(fields, printables, isMachineReadable(output))
t.print(output)
}
t := table{headers: headers} // NotificationFields are all available fields to print with NotificationsList
var NotificationFields = []string{
"id",
"status",
"updated",
for _, n := range news { // these are about the notification subject
if n.Subject == nil { "index",
continue "type",
"state",
"title",
"repository",
}
type printableNotification struct {
*gitea.NotificationThread
}
func (n printableNotification) FormatField(field string, machineReadable bool) string {
switch field {
case "id":
return fmt.Sprintf("%d", n.ID)
case "status":
status := "read"
if n.Pinned {
status = "pinned"
} else if n.Unread {
status = "unread"
} }
// if pull or Issue get Index return status
case "updated":
return FormatTime(n.UpdatedAt, machineReadable)
case "index":
var index string var index string
if n.Subject.Type == "Issue" || n.Subject.Type == "Pull" { if n.Subject.Type == "Issue" || n.Subject.Type == "Pull" {
index = n.Subject.URL index = n.Subject.URL
@ -39,31 +64,20 @@ func NotificationsList(news []*gitea.NotificationThread, output string, showRepo
if len(urlParts) != 0 { if len(urlParts) != 0 {
index = urlParts[len(urlParts)-1] index = urlParts[len(urlParts)-1]
} }
index = "#" + index
} }
return index
status := "read" case "type":
if n.Pinned { return string(n.Subject.Type)
status = "pinned"
} else if n.Unread {
status = "unread"
}
item := []string{ case "state":
fmt.Sprint(n.ID), return string(n.Subject.State)
status,
string(n.Subject.Type), case "title":
string(n.Subject.State), return n.Subject.Title
index,
n.Subject.Title, case "repo", "repository":
} return n.Repository.FullName
if showRepository {
item = append(item, n.Repository.FullName)
}
t.addRowSlice(item)
}
if t.Len() != 0 {
t.print(output)
} }
return ""
} }