Add --json support to label list (#5567)
This commit is contained in:
parent
1c9c3bf45f
commit
46e5fbdc84
4 changed files with 110 additions and 13 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package label
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
|
@ -10,6 +11,12 @@ import (
|
|||
|
||||
const maxPageSize = 100
|
||||
|
||||
var defaultFields = []string{
|
||||
"color",
|
||||
"description",
|
||||
"name",
|
||||
}
|
||||
|
||||
type listLabelsResponseData struct {
|
||||
Repository struct {
|
||||
Labels struct {
|
||||
|
|
@ -28,6 +35,8 @@ type listQueryOptions struct {
|
|||
Query string
|
||||
Order string
|
||||
Sort string
|
||||
|
||||
fields []string
|
||||
}
|
||||
|
||||
func (opts listQueryOptions) OrderBy() map[string]string {
|
||||
|
|
@ -44,17 +53,19 @@ func (opts listQueryOptions) OrderBy() map[string]string {
|
|||
// listLabels lists the labels in the given repo. Pass -1 for limit to list all labels;
|
||||
// otherwise, only that number of labels is returned for any number of pages.
|
||||
func listLabels(client *http.Client, repo ghrepo.Interface, opts listQueryOptions) ([]label, int, error) {
|
||||
if len(opts.fields) == 0 {
|
||||
opts.fields = defaultFields
|
||||
}
|
||||
|
||||
apiClient := api.NewClientFromHTTP(client)
|
||||
query := `
|
||||
fragment := fmt.Sprintf("fragment label on Label{%s}", strings.Join(opts.fields, ","))
|
||||
query := fragment + `
|
||||
query LabelList($owner: String!, $repo: String!, $limit: Int!, $endCursor: String, $query: String, $orderBy: LabelOrder) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
labels(first: $limit, after: $endCursor, query: $query, orderBy: $orderBy) {
|
||||
totalCount,
|
||||
nodes {
|
||||
name,
|
||||
color,
|
||||
description,
|
||||
createdAt,
|
||||
...label
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ type listOptions struct {
|
|||
HttpClient func() (*http.Client, error)
|
||||
IO *iostreams.IOStreams
|
||||
|
||||
Query listQueryOptions
|
||||
WebMode bool
|
||||
Exporter cmdutil.Exporter
|
||||
Query listQueryOptions
|
||||
WebMode bool
|
||||
}
|
||||
|
||||
func newCmdList(f *cmdutil.Factory, runF func(*listOptions) error) *cobra.Command {
|
||||
|
|
@ -75,11 +76,11 @@ func newCmdList(f *cmdutil.Factory, runF func(*listOptions) error) *cobra.Comman
|
|||
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List labels in the web browser")
|
||||
cmd.Flags().IntVarP(&opts.Query.Limit, "limit", "L", 30, "Maximum number of labels to fetch")
|
||||
cmd.Flags().StringVarP(&opts.Query.Query, "search", "S", "", "Search label names and descriptions")
|
||||
|
||||
// These defaults match the service behavior and, therefore, the initially release of `label list` behavior.
|
||||
cmdutil.StringEnumFlag(cmd, &opts.Query.Order, "order", "", "asc", []string{"asc", "desc"}, "Order of labels returned")
|
||||
cmdutil.StringEnumFlag(cmd, &opts.Query.Sort, "sort", "", "created", []string{"created", "name"}, "Sort fetched labels")
|
||||
|
||||
cmdutil.AddJSONFlags(cmd, &opts.Exporter, labelFields)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +105,10 @@ func listRun(opts *listOptions) error {
|
|||
return opts.Browser.Browse(labelListURL)
|
||||
}
|
||||
|
||||
if opts.Exporter != nil {
|
||||
opts.Query.fields = opts.Exporter.Fields()
|
||||
}
|
||||
|
||||
opts.IO.StartProgressIndicator()
|
||||
labels, totalCount, err := listLabels(httpClient, baseRepo, opts.Query)
|
||||
opts.IO.StopProgressIndicator()
|
||||
|
|
@ -118,6 +123,10 @@ func listRun(opts *listOptions) error {
|
|||
return cmdutil.NewNoResultsError(fmt.Sprintf("no labels found in %s", ghrepo.FullName(baseRepo)))
|
||||
}
|
||||
|
||||
if opts.Exporter != nil {
|
||||
return opts.Exporter.Write(opts.IO, labels)
|
||||
}
|
||||
|
||||
if opts.IO.IsStdoutTTY() {
|
||||
title := listHeader(ghrepo.FullName(baseRepo), len(labels), totalCount)
|
||||
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title)
|
||||
|
|
|
|||
|
|
@ -98,6 +98,39 @@ func TestNewCmdList(t *testing.T) {
|
|||
wantErr: true,
|
||||
errMsg: "cannot specify `--order` or `--sort` with `--search`",
|
||||
},
|
||||
{
|
||||
name: "json flag",
|
||||
input: "--json name,color,description,createdAt",
|
||||
output: listOptions{
|
||||
Query: listQueryOptions{
|
||||
Limit: 30,
|
||||
Order: "asc",
|
||||
Sort: "created",
|
||||
fields: []string{
|
||||
"name",
|
||||
"color",
|
||||
"description",
|
||||
"createdAt",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid json flag",
|
||||
input: "--json invalid",
|
||||
wantErr: true,
|
||||
errMsg: heredoc.Doc(`
|
||||
Unknown JSON field: "invalid"
|
||||
Available fields:
|
||||
color
|
||||
createdAt
|
||||
description
|
||||
id
|
||||
isDefault
|
||||
name
|
||||
updatedAt
|
||||
url`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,51 @@
|
|||
package label
|
||||
|
||||
type label struct {
|
||||
Name string `json:"name"`
|
||||
Color string `json:"color"`
|
||||
Description string `json:"description,omitempty"`
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var labelFields = []string{
|
||||
"color",
|
||||
"createdAt",
|
||||
"description",
|
||||
"id",
|
||||
"isDefault",
|
||||
"name",
|
||||
"updatedAt",
|
||||
"url",
|
||||
}
|
||||
|
||||
type label struct {
|
||||
Color string `json:"color"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Description string `json:"description"`
|
||||
ID string `json:"id"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// ExportData implements cmdutil.exportable
|
||||
func (l *label) ExportData(fields []string) map[string]interface{} {
|
||||
v := reflect.ValueOf(l).Elem()
|
||||
data := map[string]interface{}{}
|
||||
|
||||
for _, f := range fields {
|
||||
switch f {
|
||||
default:
|
||||
sf := fieldByName(v, f)
|
||||
data[f] = sf.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func fieldByName(v reflect.Value, field string) reflect.Value {
|
||||
return v.FieldByNameFunc(func(s string) bool {
|
||||
return strings.EqualFold(field, s)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue