From a89f51f9eca130c56c2d39194fecc6304fd60b55 Mon Sep 17 00:00:00 2001 From: Norwin Date: Fri, 3 Dec 2021 03:26:48 +0800 Subject: [PATCH] Implement more issue filters (#400) This adds new filters to `tea issues ls` and `tea pr ls`, made available in SDK 0.15: ``` --state value Filter by state (all|open|closed) (default: open) --keyword value, -k value Filter by search string --labels value, -L value Comma-separated list of labels to match issues against. --milestones value, -m value Comma-separated list of milestones to match issues against. --author value, -A value --assignee value, -a value --mentions value, -M value --from value, -F value Filter by activity after this date --until value, -u value Filter by activity before this date ``` Note: I felt free to change parameter names as exposed by SDK & API, as the names exposed by them are partially bollocks (eg `mentioned_by`) and or inconsistent with usage in other commands (eg `tea times --until`) fixes #376, related #323 Co-authored-by: Norwin Reviewed-on: https://gitea.com/gitea/tea/pulls/400 Reviewed-by: Lunny Xiao Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Norwin Co-committed-by: Norwin --- cmd/flags/csvflag.go | 8 +- cmd/flags/{flags.go => generic.go} | 98 ------------------ cmd/flags/issue_pr.go | 161 +++++++++++++++++++++++++++++ cmd/issues/list.go | 51 ++++++++- cmd/pulls/list.go | 2 +- 5 files changed, 216 insertions(+), 104 deletions(-) rename cmd/flags/{flags.go => generic.go} (57%) create mode 100644 cmd/flags/issue_pr.go diff --git a/cmd/flags/csvflag.go b/cmd/flags/csvflag.go index 99f80fb..02def8e 100644 --- a/cmd/flags/csvflag.go +++ b/cmd/flags/csvflag.go @@ -21,15 +21,19 @@ type CsvFlag struct { // NewCsvFlag creates a CsvFlag, while setting its usage string and default values func NewCsvFlag(name, usage string, aliases, availableValues, defaults []string) *CsvFlag { + var availableDesc string + if len(availableValues) != 0 { + availableDesc = " Available values:" + } return &CsvFlag{ AvailableFields: availableValues, StringFlag: cli.StringFlag{ Name: name, Aliases: aliases, Value: strings.Join(defaults, ","), - Usage: fmt.Sprintf(`Comma-separated list of %s. Available values: + Usage: fmt.Sprintf(`Comma-separated list of %s.%s %s - `, usage, strings.Join(availableValues, ",")), + `, usage, availableDesc, strings.Join(availableValues, ",")), }, } } diff --git a/cmd/flags/flags.go b/cmd/flags/generic.go similarity index 57% rename from cmd/flags/flags.go rename to cmd/flags/generic.go index c3de546..b86ab3c 100644 --- a/cmd/flags/flags.go +++ b/cmd/flags/generic.go @@ -5,14 +5,6 @@ package flags import ( - "fmt" - "strings" - - "code.gitea.io/sdk/gitea" - "code.gitea.io/tea/modules/context" - "code.gitea.io/tea/modules/task" - - "github.com/araddon/dateparse" "github.com/urfave/cli/v2" ) @@ -44,13 +36,6 @@ var OutputFlag = cli.StringFlag{ Usage: "Output format. (csv, simple, table, tsv, yaml)", } -// StateFlag provides flag to specify issue/pr state, defaulting to "open" -var StateFlag = cli.StringFlag{ - Name: "state", - Usage: "Filter by state (all|open|closed)", - DefaultText: "open", -} - // PaginationPageFlag provides flag for pagination options var PaginationPageFlag = cli.StringFlag{ Name: "page", @@ -93,13 +78,6 @@ var AllDefaultFlags = append([]cli.Flag{ &RemoteFlag, }, LoginOutputFlags...) -// IssuePRFlags defines flags that should be available on issue & pr listing flags. -var IssuePRFlags = append([]cli.Flag{ - &StateFlag, - &PaginationPageFlag, - &PaginationLimitFlag, -}, AllDefaultFlags...) - // NotificationFlags defines flags that should be available on notifications. var NotificationFlags = append([]cli.Flag{ NotificationStateFlag, @@ -121,82 +99,6 @@ var NotificationStateFlag = NewCsvFlag( []string{"unread", "pinned"}, ) -// IssuePREditFlags defines flags for properties of issues and PRs -var IssuePREditFlags = append([]cli.Flag{ - &cli.StringFlag{ - Name: "title", - Aliases: []string{"t"}, - }, - &cli.StringFlag{ - Name: "description", - Aliases: []string{"d"}, - }, - &cli.StringFlag{ - Name: "assignees", - Aliases: []string{"a"}, - Usage: "Comma-separated list of usernames to assign", - }, - &cli.StringFlag{ - Name: "labels", - Aliases: []string{"L"}, - Usage: "Comma-separated list of labels to assign", - }, - &cli.StringFlag{ - Name: "deadline", - Aliases: []string{"D"}, - Usage: "Deadline timestamp to assign", - }, - &cli.StringFlag{ - Name: "milestone", - Aliases: []string{"m"}, - Usage: "Milestone to assign", - }, -}, LoginRepoFlags...) - -// GetIssuePREditFlags parses all IssuePREditFlags -func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) { - opts := gitea.CreateIssueOption{ - Title: ctx.String("title"), - Body: ctx.String("description"), - Assignees: strings.Split(ctx.String("assignees"), ","), - } - var err error - - date := ctx.String("deadline") - if date != "" { - t, err := dateparse.ParseAny(date) - if err != nil { - return nil, err - } - opts.Deadline = &t - } - - client := ctx.Login.Client() - - labelNames := strings.Split(ctx.String("labels"), ",") - if len(labelNames) != 0 { - if client == nil { - client = ctx.Login.Client() - } - if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil { - return nil, err - } - } - - if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 { - if client == nil { - client = ctx.Login.Client() - } - ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName) - if err != nil { - return nil, fmt.Errorf("Milestone '%s' not found", milestoneName) - } - opts.Milestone = ms.ID - } - - return &opts, nil -} - // FieldsFlag generates a flag selecting printable fields. // To retrieve the value, use f.GetValues() func FieldsFlag(availableFields, defaultFields []string) *CsvFlag { diff --git a/cmd/flags/issue_pr.go b/cmd/flags/issue_pr.go new file mode 100644 index 0000000..71d1e0a --- /dev/null +++ b/cmd/flags/issue_pr.go @@ -0,0 +1,161 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package flags + +import ( + "fmt" + "strings" + + "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/task" + + "github.com/araddon/dateparse" + "github.com/urfave/cli/v2" +) + +// StateFlag provides flag to specify issue/pr state, defaulting to "open" +var StateFlag = cli.StringFlag{ + Name: "state", + Usage: "Filter by state (all|open|closed)", + DefaultText: "open", +} + +// MilestoneFilterFlag is a CSV flag used to filter issues by milestones +var MilestoneFilterFlag = NewCsvFlag( + "milestones", + "milestones to match issues against", + []string{"m"}, nil, nil) + +// LabelFilterFlag is a CSV flag used to filter issues by labels +var LabelFilterFlag = NewCsvFlag( + "labels", + "labels to match issues against", + []string{"L"}, nil, nil) + +// PRListingFlags defines flags that should be available on pr listing flags. +var PRListingFlags = append([]cli.Flag{ + &StateFlag, + &PaginationPageFlag, + &PaginationLimitFlag, +}, AllDefaultFlags...) + +// IssueListingFlags defines flags that should be available on issue listing flags. +var IssueListingFlags = append([]cli.Flag{ + &StateFlag, + &cli.StringFlag{ + Name: "kind", + Aliases: []string{"K"}, + Usage: "Wether to return `issues`, `pulls`, or `all` (you can use this to apply advanced search filters to PRs)", + DefaultText: "issues", + }, + &cli.StringFlag{ + Name: "keyword", + Aliases: []string{"k"}, + Usage: "Filter by search string", + }, + LabelFilterFlag, + MilestoneFilterFlag, + &cli.StringFlag{ + Name: "author", + Aliases: []string{"A"}, + }, + &cli.StringFlag{ + Name: "assignee", + Aliases: []string{"a"}, + }, + &cli.StringFlag{ + Name: "mentions", + Aliases: []string{"M"}, + }, + &cli.StringFlag{ + Name: "from", + Aliases: []string{"F"}, + Usage: "Filter by activity after this date", + }, + &cli.StringFlag{ + Name: "until", + Aliases: []string{"u"}, + Usage: "Filter by activity before this date", + }, + &PaginationPageFlag, + &PaginationLimitFlag, +}, AllDefaultFlags...) + +// IssuePREditFlags defines flags for properties of issues and PRs +var IssuePREditFlags = append([]cli.Flag{ + &cli.StringFlag{ + Name: "title", + Aliases: []string{"t"}, + }, + &cli.StringFlag{ + Name: "description", + Aliases: []string{"d"}, + }, + &cli.StringFlag{ + Name: "assignees", + Aliases: []string{"a"}, + Usage: "Comma-separated list of usernames to assign", + }, + &cli.StringFlag{ + Name: "labels", + Aliases: []string{"L"}, + Usage: "Comma-separated list of labels to assign", + }, + &cli.StringFlag{ + Name: "deadline", + Aliases: []string{"D"}, + Usage: "Deadline timestamp to assign", + }, + &cli.StringFlag{ + Name: "milestone", + Aliases: []string{"m"}, + Usage: "Milestone to assign", + }, +}, LoginRepoFlags...) + +// GetIssuePREditFlags parses all IssuePREditFlags +func GetIssuePREditFlags(ctx *context.TeaContext) (*gitea.CreateIssueOption, error) { + opts := gitea.CreateIssueOption{ + Title: ctx.String("title"), + Body: ctx.String("description"), + Assignees: strings.Split(ctx.String("assignees"), ","), + } + var err error + + date := ctx.String("deadline") + if date != "" { + t, err := dateparse.ParseAny(date) + if err != nil { + return nil, err + } + opts.Deadline = &t + } + + client := ctx.Login.Client() + + labelNames := strings.Split(ctx.String("labels"), ",") + if len(labelNames) != 0 { + if client == nil { + client = ctx.Login.Client() + } + if opts.Labels, err = task.ResolveLabelNames(client, ctx.Owner, ctx.Repo, labelNames); err != nil { + return nil, err + } + } + + if milestoneName := ctx.String("milestone"); len(milestoneName) != 0 { + if client == nil { + client = ctx.Login.Client() + } + ms, _, err := client.GetMilestoneByName(ctx.Owner, ctx.Repo, milestoneName) + if err != nil { + return nil, fmt.Errorf("Milestone '%s' not found", milestoneName) + } + opts.Milestone = ms.ID + } + + return &opts, nil +} diff --git a/cmd/issues/list.go b/cmd/issues/list.go index 3089af3..e5999fb 100644 --- a/cmd/issues/list.go +++ b/cmd/issues/list.go @@ -5,11 +5,15 @@ package issues import ( + "fmt" + "time" + "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" "code.gitea.io/sdk/gitea" + "github.com/araddon/dateparse" "github.com/urfave/cli/v2" ) @@ -24,7 +28,7 @@ var CmdIssuesList = cli.Command{ Usage: "List issues of the repository", Description: `List issues of the repository`, Action: RunIssuesList, - Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssuePRFlags...), + Flags: append([]cli.Flag{issueFieldsFlag}, flags.IssueListingFlags...), } // RunIssuesList list issues @@ -36,16 +40,57 @@ func RunIssuesList(cmd *cli.Context) error { switch ctx.String("state") { case "all": state = gitea.StateAll - case "open": + case "", "open": state = gitea.StateOpen case "closed": state = gitea.StateClosed + default: + return fmt.Errorf("unknown state '%s'", ctx.String("state")) } + kind := gitea.IssueTypeIssue + switch ctx.String("kind") { + case "", "issues", "issue": + kind = gitea.IssueTypeIssue + case "pulls", "pull", "pr": + kind = gitea.IssueTypePull + case "all": + kind = gitea.IssueTypeAll + default: + return fmt.Errorf("unknown kind '%s'", ctx.String("kind")) + } + + var err error + var from, until time.Time + if ctx.IsSet("from") { + from, err = dateparse.ParseLocal(ctx.String("from")) + if err != nil { + return err + } + } + if ctx.IsSet("until") { + until, err = dateparse.ParseLocal(ctx.String("until")) + if err != nil { + return err + } + } + + // ignore error, as we don't do any input validation on these flags + labels, _ := flags.LabelFilterFlag.GetValues(cmd) + milestones, _ := flags.MilestoneFilterFlag.GetValues(cmd) + issues, _, err := ctx.Login.Client().ListRepoIssues(ctx.Owner, ctx.Repo, gitea.ListIssueOption{ ListOptions: ctx.GetListOptions(), State: state, - Type: gitea.IssueTypeIssue, + Type: kind, + KeyWord: ctx.String("keyword"), + CreatedBy: ctx.String("author"), + AssignedBy: ctx.String("assigned-to"), + MentionedBy: ctx.String("mentions"), + Labels: labels, + Milestones: milestones, + Since: from, + Before: until, }) if err != nil { diff --git a/cmd/pulls/list.go b/cmd/pulls/list.go index 54f3a1a..b09f6bc 100644 --- a/cmd/pulls/list.go +++ b/cmd/pulls/list.go @@ -24,7 +24,7 @@ var CmdPullsList = cli.Command{ Usage: "List pull requests of the repository", Description: `List pull requests of the repository`, Action: RunPullsList, - Flags: append([]cli.Flag{pullFieldsFlag}, flags.IssuePRFlags...), + Flags: append([]cli.Flag{pullFieldsFlag}, flags.PRListingFlags...), } // RunPullsList return list of pulls