diff --git a/pkg/template/template.go b/pkg/template/template.go index a561ebb..7dfd89a 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -4,6 +4,7 @@ package template import ( + "bytes" "encoding/json" "fmt" "io" @@ -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" @@ -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 diff --git a/pkg/template/template_test.go b/pkg/template/template_test.go index 9e5dcad..bc9af2b 100644 --- a/pkg/template/template_test.go +++ b/pkg/template/template_test.go @@ -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