Skip to content

Commit

Permalink
Support filtering before applying template
Browse files Browse the repository at this point in the history
Resolves cli/cli#8415
  • Loading branch information
heaths committed Jan 19, 2025
1 parent 13104ed commit 9dda274
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 1 deletion.
19 changes: 18 additions & 1 deletion pkg/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package template

import (
"bytes"
"encoding/json"
"fmt"
"io"
Expand All @@ -13,6 +14,7 @@ import (
"text/template"
"time"

"github.com/cli/go-gh/v2/pkg/jq"
"github.com/cli/go-gh/v2/pkg/tableprinter"
"github.com/cli/go-gh/v2/pkg/text"
color "github.com/mgutz/ansi"
Expand Down Expand Up @@ -91,9 +93,24 @@ func (t *Template) Parse(tmpl string) error {
return err
}

// Execute applies the parsed template to the input and writes result to the writer
// Execute applies the parsed template to the input and writes the result to the writer
// the template was initialized with.
func (t *Template) Execute(input io.Reader) error {
return t.ExecuteFiltered(input, "")
}

// ExecuteFiltered filters the input using the `jq` package then applies the parsed template
// to the input and writes the result to the writer the template was initialized with.
func (t *Template) ExecuteFiltered(input io.Reader, filter string) error {
if filter != "" {
buf := bytes.Buffer{}
if err := jq.Evaluate(input, &buf, filter); err != nil {
return err
}

input = io.Reader(&buf)
}

jsonData, err := io.ReadAll(input)
if err != nil {
return err
Expand Down
76 changes: 76 additions & 0 deletions pkg/template/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,82 @@ func TestExecute(t *testing.T) {
}
}

func TestExecuteFiltered(t *testing.T) {
type args struct {
json io.Reader
template string
filter string
colorize bool
}
tests := []struct {
name string
args args
wantW string
wantErr bool
}{
{
name: "without filter",
args: args{
json: strings.NewReader(`[{"name":"b"},{"name":"a"}]`),
template: `{{range .}}{{.name}}{{end}}`,
},
wantW: "ba",
},
{
name: "with filter",
args: args{
json: strings.NewReader(`[{"name":"b"},{"name":"a"}]`),
filter: `sort_by(.name)`,
template: `{{range .}}{{.name}}{{end}}`,
},
wantW: "ab",
},
{
name: "color with filter",
args: args{
json: strings.NewReader(`{"error":"an error occurred"}`),
filter: `{error: .error | ascii_upcase}`,
template: `{{color "red" .error}}`,
},
wantW: "\x1b[0;31mAN ERROR OCCURRED\x1b[0m",
},
{
name: "filter error",
args: args{
json: strings.NewReader(`{}`),
filter: "nofunc",
template: "{{.}}",
},
wantErr: true,
},
{
name: "template error",
args: args{
json: strings.NewReader(`{}`),
template: `{{ truncate }}`,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{}
tmpl := New(w, 80, tt.args.colorize)
err := tmpl.Parse(tt.args.template)
assert.NoError(t, err)
err = tmpl.ExecuteFiltered(tt.args.json, tt.args.filter)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
err = tmpl.Flush()
assert.NoError(t, err)
assert.Equal(t, tt.wantW, w.String())
})
}
}

func TestTruncateMultiline(t *testing.T) {
type args struct {
max int
Expand Down

0 comments on commit 9dda274

Please sign in to comment.