Skip to content

Commit 8ae1875

Browse files
authored
feat: Support CSV when listing issues (#854)
1 parent 08a6c01 commit 8ae1875

File tree

6 files changed

+54
-10
lines changed

6 files changed

+54
-10
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ $ jira issue list -s"To Do"
177177
# List recent issues in plain mode
178178
$ jira issue list --plain
179179

180+
# List recent issues in raw JSON format
181+
$ jira issue list --raw
182+
183+
# List recent issues in csv format
184+
$ jira issue list --csv
185+
180186
# List issue in the same order as you see in the UI
181187
$ jira issue list --order-by rank --reverse
182188

internal/cmd/epic/list/list.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ func singleEpicView(flags query.FlagParser, key, project, projectType, server st
127127
plain, err := flags.GetBool("plain")
128128
cmdutil.ExitIfError(err)
129129

130+
csv, err := flags.GetBool("csv")
131+
cmdutil.ExitIfError(err)
132+
130133
noHeaders, err := flags.GetBool("no-headers")
131134
cmdutil.ExitIfError(err)
132135

@@ -149,6 +152,7 @@ func singleEpicView(flags query.FlagParser, key, project, projectType, server st
149152
},
150153
Display: view.DisplayFormat{
151154
Plain: plain,
155+
CSV: csv,
152156
NoHeaders: noHeaders,
153157
NoTruncate: noTruncate,
154158
FixedColumns: fixedColumns,

internal/cmd/issue/list/list.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ func loadList(cmd *cobra.Command, args []string) {
141141
plain, err := cmd.Flags().GetBool("plain")
142142
cmdutil.ExitIfError(err)
143143

144+
csv, err := cmd.Flags().GetBool("csv")
145+
cmdutil.ExitIfError(err)
146+
144147
noHeaders, err := cmd.Flags().GetBool("no-headers")
145148
cmdutil.ExitIfError(err)
146149

@@ -171,6 +174,7 @@ func loadList(cmd *cobra.Command, args []string) {
171174
},
172175
Display: view.DisplayFormat{
173176
Plain: plain,
177+
CSV: csv,
174178
NoHeaders: noHeaders,
175179
NoTruncate: noTruncate,
176180
FixedColumns: fixedColumns,
@@ -234,6 +238,7 @@ func SetFlags(cmd *cobra.Command) {
234238
cmd.Flags().Bool("no-truncate", false, "Show all available columns in plain mode. Works only with --plain")
235239
cmd.Flags().Uint("comments", 1, "Show N comments when viewing the issue")
236240
cmd.Flags().Bool("raw", false, "Print raw JSON output")
241+
cmd.Flags().Bool("csv", false, "Print output in CSV format")
237242

238243
if cmd.HasParent() && cmd.Parent().Name() != "sprint" {
239244
cmd.Flags().String("columns", "", "Comma separated list of columns to display in the plain mode.\n"+

internal/cmd/sprint/list/list.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ func singleSprintView(sprintQuery *query.Sprint, flags query.FlagParser, boardID
121121
plain, err := flags.GetBool("plain")
122122
cmdutil.ExitIfError(err)
123123

124+
csv, err := flags.GetBool("csv")
125+
cmdutil.ExitIfError(err)
126+
124127
noHeaders, err := flags.GetBool("no-headers")
125128
cmdutil.ExitIfError(err)
126129

@@ -166,6 +169,7 @@ func singleSprintView(sprintQuery *query.Sprint, flags query.FlagParser, boardID
166169
},
167170
Display: view.DisplayFormat{
168171
Plain: plain,
172+
CSV: csv,
169173
NoHeaders: noHeaders,
170174
NoTruncate: noTruncate,
171175
FixedColumns: fixedColumns,

internal/view/helper.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package view
22

33
import (
4+
"encoding/csv"
45
"fmt"
56
"io"
67
"os"
@@ -116,7 +117,7 @@ func prepareTitle(text string) string {
116117
return tview.Escape(text)
117118
}
118119

119-
func issueKeyFromTuiData(r int, d interface{}) string {
120+
func issueKeyFromTuiData(r int, d any) string {
120121
var path string
121122

122123
switch data := d.(type) {
@@ -129,24 +130,24 @@ func issueKeyFromTuiData(r int, d interface{}) string {
129130
return path
130131
}
131132

132-
func jiraURLFromTuiData(server string, r int, d interface{}) string {
133+
func jiraURLFromTuiData(server string, r int, d any) string {
133134
return cmdutil.GenerateServerBrowseURL(server, issueKeyFromTuiData(r, d))
134135
}
135136

136137
func navigate(server string) tui.SelectedFunc {
137-
return func(r, _ int, d interface{}) {
138+
return func(r, _ int, d any) {
138139
_ = browser.Browse(jiraURLFromTuiData(server, r, d))
139140
}
140141
}
141142

142143
func copyURL(server string) tui.CopyFunc {
143-
return func(r, _ int, d interface{}) {
144+
return func(r, _ int, d any) {
144145
_ = clipboard.WriteAll(jiraURLFromTuiData(server, r, d))
145146
}
146147
}
147148

148149
func copyKey() tui.CopyKeyFunc {
149-
return func(r, _ int, d interface{}) {
150+
return func(r, _ int, d any) {
150151
_ = clipboard.WriteAll(issueKeyFromTuiData(r, d))
151152
}
152153
}
@@ -169,6 +170,22 @@ func renderPlain(w io.Writer, data tui.TableData) error {
169170
return nil
170171
}
171172

173+
func renderCSV(w io.Writer, data tui.TableData) error {
174+
csvwrt := csv.NewWriter(w)
175+
176+
for _, items := range data {
177+
if err := csvwrt.Write(items); err != nil {
178+
return err
179+
}
180+
}
181+
182+
csvwrt.Flush()
183+
if err := csvwrt.Error(); err != nil {
184+
return err
185+
}
186+
return nil
187+
}
188+
172189
func unescape(s string) string {
173190
pattern := regexp.MustCompile(`(\[[a-zA-Z0-9_,;: \-\."#]+\[*)\[\]`)
174191
return pattern.ReplaceAllString(s, "$1]")

internal/view/issues.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
// DisplayFormat is a issue display type.
1717
type DisplayFormat struct {
1818
Plain bool
19+
CSV bool
1920
NoHeaders bool
2021
NoTruncate bool
2122
Columns []string
@@ -43,6 +44,11 @@ func (l *IssueList) Render() error {
4344
return l.renderPlain(w)
4445
}
4546

47+
if l.Display.CSV {
48+
w := os.Stdout
49+
return l.renderCSV(w)
50+
}
51+
4652
renderer, err := MDRenderer()
4753
if err != nil {
4854
return err
@@ -123,11 +129,16 @@ func (l *IssueList) Render() error {
123129
return view.Paint(data)
124130
}
125131

126-
// renderPlain renders the issue in plain view.
132+
// renderPlain renders issues in plain formatted view.
127133
func (l *IssueList) renderPlain(w io.Writer) error {
128134
return renderPlain(w, l.data())
129135
}
130136

137+
// renderCSV renders issues in csv format.
138+
func (l *IssueList) renderCSV(w io.Writer) error {
139+
return renderCSV(w, l.data())
140+
}
141+
131142
func (*IssueList) validColumnsMap() map[string]struct{} {
132143
columns := ValidIssueColumns()
133144
out := make(map[string]struct{}, len(columns))
@@ -177,12 +188,9 @@ func (l *IssueList) data() tui.TableData {
177188
var data tui.TableData
178189

179190
headers := l.header()
180-
if !l.Display.Plain || !l.Display.NoHeaders {
191+
if (!l.Display.Plain && !l.Display.CSV) || !l.Display.NoHeaders {
181192
data = append(data, headers)
182193
}
183-
if len(headers) == 0 {
184-
headers = ValidIssueColumns()
185-
}
186194
for _, iss := range l.Data {
187195
data = append(data, l.assignColumns(headers, iss))
188196
}

0 commit comments

Comments
 (0)