Add --json support to label list (#5567)

This commit is contained in:
Heath Stewart 2022-05-04 06:02:55 -07:00 committed by GitHub
parent 1c9c3bf45f
commit 46e5fbdc84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 13 deletions

View file

@ -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

View file

@ -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)

View file

@ -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 {

View file

@ -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)
})
}