Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/cmd/projectList.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ import (

"github.com/zeropsio/zcli/src/cmdBuilder"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zcli/src/output"
"github.com/zeropsio/zcli/src/uxHelpers"
)

func projectListCmd() *cmdBuilder.Cmd {
return cmdBuilder.NewCmd().
Use("list").
Short(i18n.T(i18n.CmdDescProjectList)).
StringFlag("output", "table", i18n.T(i18n.OutputFormatFlag)).
HelpFlag(i18n.T(i18n.CmdHelpProjectList)).
LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error {
err := uxHelpers.PrintProjectList(ctx, cmdData.RestApiClient, cmdData.Stdout)
outputFormat, err := output.ParseFormat(cmdData.Params.GetString("output"))
if err != nil {
return err
}

err = uxHelpers.PrintProjectList(ctx, cmdData.RestApiClient, cmdData.Stdout, outputFormat)
if err != nil {
return err
}
Expand Down
8 changes: 8 additions & 0 deletions src/cmd/serviceList.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/zeropsio/zcli/src/cmdBuilder"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zcli/src/output"
"github.com/zeropsio/zcli/src/uxHelpers"
)

Expand All @@ -14,8 +15,14 @@ func serviceListCmd() *cmdBuilder.Cmd {
Short(i18n.T(i18n.CmdDescServiceList)).
ScopeLevel(cmdBuilder.ScopeProject()).
Arg(cmdBuilder.ProjectArgName, cmdBuilder.OptionalArg()).
StringFlag("output", "table", i18n.T(i18n.OutputFormatFlag)).
HelpFlag(i18n.T(i18n.CmdHelpServiceList)).
LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error {
outputFormat, err := output.ParseFormat(cmdData.Params.GetString("output"))
if err != nil {
return err
}

project, err := cmdData.Project.Expect("project is null")
if err != nil {
return err
Expand All @@ -25,6 +32,7 @@ func serviceListCmd() *cmdBuilder.Cmd {
cmdData.RestApiClient,
cmdData.Stdout,
project,
outputFormat,
); err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ at https://docs.zerops.io/references/cli for further details.`,
VerboseFlag: "If set, additional data will be logged to the zcli debug log file.",
ZeropsYamlSetup: "Choose setup to be used from zerops.yml.",
DisableLogs: "Disable log output.",
OutputFormatFlag: "Output format. Supported: table, json, csv.",

// archiveClient
ArchClientWorkingDirectory: "working directory: %s",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/i18n.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ const (
VerboseFlag = "VerboseFlag"
ZeropsYamlSetup = "ZeropsYamlSetup"
DisableLogs = "DisableLogs"
OutputFormatFlag = "OutputFormatFlag"

// archiveClient
ArchClientWorkingDirectory = "ArchClientWorkingDirectory"
Expand Down
88 changes: 88 additions & 0 deletions src/output/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package output

import (
"encoding/csv"
"encoding/json"
"fmt"
"strings"

"github.com/zeropsio/zcli/src/uxBlock/models/table"
)

type Format string

const (
FormatTable Format = "table"
FormatJSON Format = "json"
FormatCSV Format = "csv"
)

func ParseFormat(s string) (Format, error) {
switch strings.ToLower(s) {
case "", "table":
return FormatTable, nil
case "json":
return FormatJSON, nil
case "csv":
return FormatCSV, nil
default:
return FormatTable, fmt.Errorf("unsupported output format %q (supported: %s)", s, SupportedFormats())
}
}

func SupportedFormats() string {
return "table, json, csv"
}

func PrintData(headers []string, rows [][]string, format Format) (string, error) {
switch format {
case FormatJSON:
return printJSON(headers, rows)
case FormatCSV:
return printCSV(headers, rows)
default:
return printTable(headers, rows), nil
}
}

func printTable(headers []string, rows [][]string) string {
headerRow := table.NewRowFromStrings(headers...)
body := table.NewBody()
for _, row := range rows {
body.AddStringsRow(row...)
}
return table.Render(body, table.WithHeader(headerRow))
}

func printJSON(headers []string, rows [][]string) (string, error) {
items := make([]map[string]string, 0, len(rows))
for _, row := range rows {
item := make(map[string]string, len(headers))
for i, h := range headers {
if i < len(row) {
item[h] = row[i]
}
}
items = append(items, item)
}
out, err := json.MarshalIndent(items, "", " ")
if err != nil {
return "", err
}
return string(out), nil
}

func printCSV(headers []string, rows [][]string) (string, error) {
var b strings.Builder
w := csv.NewWriter(&b)
if err := w.Write(headers); err != nil {
return "", err
}
for _, row := range rows {
if err := w.Write(row); err != nil {
return "", err
}
}
w.Flush()
return b.String(), w.Error()
}
102 changes: 102 additions & 0 deletions src/output/formatter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package output

import (
"encoding/json"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

func TestParseFormat(t *testing.T) {
tests := []struct {
input string
want Format
wantErr bool
}{
{"", FormatTable, false},
{"table", FormatTable, false},
{"json", FormatJSON, false},
{"csv", FormatCSV, false},
{"JSON", FormatJSON, false},
{"CSV", FormatCSV, false},
{"yaml", FormatTable, true},
{"xml", FormatTable, true},
}
for _, tt := range tests {
got, err := ParseFormat(tt.input)
if tt.wantErr {
require.Error(t, err, "input %q", tt.input)
} else {
require.NoError(t, err, "input %q", tt.input)
require.Equal(t, tt.want, got, "input %q", tt.input)
}
}
}

func TestPrintDataTable(t *testing.T) {
headers := []string{"id", "name", "status"}
rows := [][]string{
{"1", "foo", "active"},
{"2", "bar", "inactive"},
}
result, err := PrintData(headers, rows, FormatTable)
require.NoError(t, err)
require.Contains(t, result, "ID")
require.Contains(t, result, "foo")
require.Contains(t, result, "bar")
}

func TestPrintDataJSON(t *testing.T) {
headers := []string{"id", "name", "status"}
rows := [][]string{
{"1", "foo", "active"},
{"2", "bar", "inactive"},
}
result, err := PrintData(headers, rows, FormatJSON)
require.NoError(t, err)

require.Contains(t, result, `"id": "1"`)
require.Contains(t, result, `"name": "foo"`)
require.Contains(t, result, `"status": "active"`)
require.Contains(t, result, `"id": "2"`)
require.Contains(t, result, `"name": "bar"`)

var parsed []map[string]interface{}
require.NoError(t, json.Unmarshal([]byte(result), &parsed))
require.Len(t, parsed, 2)
}

func TestPrintDataCSV(t *testing.T) {
headers := []string{"id", "name", "status"}
rows := [][]string{
{"1", "foo", "active"},
{"2", "bar", "inactive"},
}
result, err := PrintData(headers, rows, FormatCSV)
require.NoError(t, err)

lines := strings.Split(strings.TrimSpace(result), "\n")
require.Len(t, lines, 3)
require.Equal(t, "id,name,status", lines[0])
require.Equal(t, "1,foo,active", lines[1])
require.Equal(t, "2,bar,inactive", lines[2])
}

func TestPrintDataEmpty(t *testing.T) {
headers := []string{"id", "name"}
var rows [][]string

result, err := PrintData(headers, rows, FormatJSON)
require.NoError(t, err)
require.Equal(t, "[]", result)

result, err = PrintData(headers, rows, FormatCSV)
require.NoError(t, err)
require.Equal(t, "id,name\n", result)

result, err = PrintData(headers, rows, FormatTable)
require.NoError(t, err)
require.Contains(t, result, "ID")
require.Contains(t, result, "NAME")
}
32 changes: 30 additions & 2 deletions src/uxHelpers/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/zeropsio/zcli/src/gn"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zcli/src/optional"
"github.com/zeropsio/zcli/src/output"
"github.com/zeropsio/zcli/src/uxBlock"
"github.com/zeropsio/zcli/src/uxBlock/models/selector"
"github.com/zeropsio/zcli/src/uxBlock/models/table"
Expand Down Expand Up @@ -96,6 +97,7 @@ func PrintProjectList(
ctx context.Context,
restApiClient *zeropsRestApiClient.Handler,
out io.Writer,
format output.Format,
) error {
projects, err := repository.GetAllProjects(ctx, restApiClient)
if err != nil {
Expand All @@ -104,12 +106,38 @@ func PrintProjectList(

header, body := createProjectTableRows(projects, false)

t := table.Render(body, table.WithHeader(header))
headers := extractHeaders(header)
rows := extractRows(body)

_, err = fmt.Fprintln(out, t)
result, err := output.PrintData(headers, rows, format)
if err != nil {
return err
}

_, err = fmt.Fprintln(out, result)
return err
}

func extractHeaders(header *table.Row) []string {
var h []string
for _, c := range header.Cells() {
h = append(h, c.String())
}
return h
}

func extractRows(body *table.Body) [][]string {
var rows [][]string
for _, r := range body.Rows() {
var row []string
for _, c := range r.Cells() {
row = append(row, c.String())
}
rows = append(rows, row)
}
return rows
}

func createProjectTableRows(projects []entity.Project, createNewProject bool) (*table.Row, *table.Body) {
header := table.NewRowFromStrings("id", "name", "org name", "org id", "status", "mode")

Expand Down
12 changes: 10 additions & 2 deletions src/uxHelpers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/zeropsio/zcli/src/gn"
"github.com/zeropsio/zcli/src/i18n"
"github.com/zeropsio/zcli/src/optional"
"github.com/zeropsio/zcli/src/output"
"github.com/zeropsio/zcli/src/uxBlock"
"github.com/zeropsio/zcli/src/uxBlock/models/selector"
"github.com/zeropsio/zcli/src/uxBlock/models/table"
Expand Down Expand Up @@ -75,6 +76,7 @@ func PrintServiceList(
restApiClient *zeropsRestApiClient.Handler,
out io.Writer,
project entity.Project,
format output.Format,
) error {
services, err := repository.GetNonSystemServicesByProject(ctx, restApiClient, project)
if err != nil {
Expand All @@ -83,9 +85,15 @@ func PrintServiceList(

header, body := createServiceTableRows(services, false)

t := table.Render(body, table.WithHeader(header))
headers := extractHeaders(header)
rows := extractRows(body)

_, err = fmt.Fprintln(out, t)
result, err := output.PrintData(headers, rows, format)
if err != nil {
return err
}

_, err = fmt.Fprintln(out, result)
return err
}

Expand Down