Skip to content

Commit

Permalink
Provide global output flag for all cli commands (#1021)
Browse files Browse the repository at this point in the history
Provide a consistent way for users to set the output format globally
across all CLI commands. Implement JSON and YAML options for the
output flag. Update validation for user input options to be global.
Apply new output formats to: context ls, project create/ls/update,
document ls, and history ls commands.
  • Loading branch information
TaeyeonRoyce authored Sep 30, 2024
1 parent 2a29c7d commit d8414e1
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 120 deletions.
17 changes: 17 additions & 0 deletions cmd/yorkie/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package main

import (
"errors"
"os"
"path"

Expand Down Expand Up @@ -55,4 +56,20 @@ func init() {

rootCmd.PersistentFlags().String("rpc-addr", "localhost:8080", "Address of the rpc server")
_ = viper.BindPFlag("rpcAddr", rootCmd.PersistentFlags().Lookup("rpc-addr"))

rootCmd.PersistentFlags().StringP("output", "o", "", "One of 'yaml' or 'json'.")
_ = viper.BindPFlag("output", rootCmd.PersistentFlags().Lookup("output"))

rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
return validateOutputOpts()
}
}

// validateOutputOpts validates the output options.
func validateOutputOpts() error {
output := viper.GetString("output")
if output != DefaultOutput && output != YamlOutput && output != JSONOutput {
return errors.New(`--output must be 'yaml' or 'json'`)
}
return nil
}
64 changes: 54 additions & 10 deletions cmd/yorkie/context/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,24 @@
package context

import (
"encoding/json"
"fmt"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"

"github.com/yorkie-team/yorkie/cmd/yorkie/config"
)

type contextInfo struct {
Current string `json:"current" yaml:"current"`
RPCAddr string `json:"rpc_addr" yaml:"rpc_addr"`
Insecure string `json:"insecure" yaml:"insecure"`
Token string `json:"token" yaml:"token"`
}

func newListCommand() *cobra.Command {
return &cobra.Command{
Use: "ls",
Expand All @@ -37,14 +46,7 @@ func newListCommand() *cobra.Command {
return err
}

tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false

tw.AppendHeader(table.Row{"CURRENT", "RPC ADDR", "INSECURE", "TOKEN"})
contexts := make([]contextInfo, 0, len(conf.Auths))
for rpcAddr, auth := range conf.Auths {
current := ""
if rpcAddr == viper.GetString("rpcAddr") {
Expand All @@ -61,16 +63,58 @@ func newListCommand() *cobra.Command {
ellipsisToken = auth.Token[:10] + "..." + auth.Token[len(auth.Token)-10:]
}

tw.AppendRow(table.Row{current, rpcAddr, insecure, ellipsisToken})
contexts = append(contexts, contextInfo{
Current: current,
RPCAddr: rpcAddr,
Insecure: insecure,
Token: ellipsisToken,
})
}

fmt.Println(tw.Render())
output := viper.GetString("output")
if err := printContexts(cmd, output, contexts); err != nil {
return err
}

return nil
},
}
}

func printContexts(cmd *cobra.Command, output string, contexts []contextInfo) error {
switch output {
case "":
tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false

tw.AppendHeader(table.Row{"CURRENT", "RPC ADDR", "INSECURE", "TOKEN"})
for _, ctx := range contexts {
tw.AppendRow(table.Row{ctx.Current, ctx.RPCAddr, ctx.Insecure, ctx.Token})
}
cmd.Println(tw.Render())
case "json":
marshalled, err := json.MarshalIndent(contexts, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %w", err)
}
cmd.Println(string(marshalled))
case "yaml":
marshalled, err := yaml.Marshal(contexts)
if err != nil {
return fmt.Errorf("failed to marshal YAML: %w", err)
}
cmd.Println(string(marshalled))
default:
return fmt.Errorf("unknown output format: %s", output)
}

return nil
}

func init() {
SubCmd.AddCommand(newListCommand())
}
79 changes: 55 additions & 24 deletions cmd/yorkie/document/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ package document

import (
"context"
"encoding/json"
"errors"
"fmt"
"time"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"

"github.com/yorkie-team/yorkie/admin"
"github.com/yorkie-team/yorkie/api/types"
"github.com/yorkie-team/yorkie/cmd/yorkie/config"
"github.com/yorkie-team/yorkie/pkg/units"
)
Expand Down Expand Up @@ -68,36 +72,63 @@ func newListCommand() *cobra.Command {
return err
}

tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false
tw.AppendHeader(table.Row{
"ID",
"KEY",
"CREATED AT",
"ACCESSED AT",
"UPDATED AT",
"SNAPSHOT",
})
for _, document := range documents {
tw.AppendRow(table.Row{
document.ID,
document.Key,
units.HumanDuration(time.Now().UTC().Sub(document.CreatedAt)),
units.HumanDuration(time.Now().UTC().Sub(document.AccessedAt)),
units.HumanDuration(time.Now().UTC().Sub(document.UpdatedAt)),
document.Snapshot,
})
output := viper.GetString("output")
if err := printDocuments(cmd, output, documents); err != nil {
return err
}
cmd.Printf("%s\n", tw.Render())

return nil
},
}
}

func printDocuments(cmd *cobra.Command, output string, documents []*types.DocumentSummary) error {
switch output {
case "":
tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false
tw.AppendHeader(table.Row{
"ID",
"KEY",
"CREATED AT",
"ACCESSED AT",
"UPDATED AT",
"SNAPSHOT",
})
for _, document := range documents {
tw.AppendRow(table.Row{
document.ID,
document.Key,
units.HumanDuration(time.Now().UTC().Sub(document.CreatedAt)),
units.HumanDuration(time.Now().UTC().Sub(document.AccessedAt)),
units.HumanDuration(time.Now().UTC().Sub(document.UpdatedAt)),
document.Snapshot,
})
}
cmd.Printf("%s\n", tw.Render())
case "json":
jsonOutput, err := json.MarshalIndent(documents, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %w", err)
}
cmd.Println(string(jsonOutput))
case "yaml":
yamlOutput, err := yaml.Marshal(documents)
if err != nil {
return fmt.Errorf("failed to marshal YAML: %w", err)
}
cmd.Println(string(yamlOutput))
default:
return fmt.Errorf("unknown output format: %s", output)
}

return nil
}

func init() {
cmd := newListCommand()
cmd.Flags().StringVar(
Expand Down
68 changes: 49 additions & 19 deletions cmd/yorkie/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ package main

import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"

"github.com/yorkie-team/yorkie/admin"
"github.com/yorkie-team/yorkie/api/types"
"github.com/yorkie-team/yorkie/cmd/yorkie/config"
"github.com/yorkie-team/yorkie/pkg/document/key"
)
Expand All @@ -44,7 +48,6 @@ func newHistoryCmd() *cobra.Command {
if len(args) != 2 {
return errors.New("project name and document key are required")
}

rpcAddr := viper.GetString("rpcAddr")
auth, err := config.LoadAuth(rpcAddr)
if err != nil {
Expand Down Expand Up @@ -72,30 +75,57 @@ func newHistoryCmd() *cobra.Command {
return err
}

tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false
tw.AppendHeader(table.Row{
"SEQ",
"MESSAGE",
"SNAPSHOT",
})
for _, change := range changes {
tw.AppendRow(table.Row{
change.ID.ServerSeq(),
change.Message,
change.Snapshot,
})
output := viper.GetString("output")
if err := printHistories(cmd, output, changes); err != nil {
return err
}
cmd.Printf("%s\n", tw.Render())

return nil
},
}
}

func printHistories(cmd *cobra.Command, output string, changes []*types.ChangeSummary) error {
switch output {
case DefaultOutput:
tw := table.NewWriter()
tw.Style().Options.DrawBorder = false
tw.Style().Options.SeparateColumns = false
tw.Style().Options.SeparateFooter = false
tw.Style().Options.SeparateHeader = false
tw.Style().Options.SeparateRows = false
tw.AppendHeader(table.Row{
"SEQ",
"MESSAGE",
"SNAPSHOT",
})
for _, change := range changes {
tw.AppendRow(table.Row{
change.ID.ServerSeq(),
change.Message,
change.Snapshot,
})
}
cmd.Printf("%s\n", tw.Render())
case JSONOutput:
jsonOutput, err := json.MarshalIndent(changes, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal JSON: %w", err)
}
cmd.Println(string(jsonOutput))
case YamlOutput:
yamlOutput, err := yaml.Marshal(changes)
if err != nil {
return fmt.Errorf("failed to marshal YAML: %w", err)
}
cmd.Println(string(yamlOutput))
default:
return fmt.Errorf("unknown output format: %s", output)
}

return nil
}

func init() {
cmd := newHistoryCmd()
cmd.Flags().Int64Var(
Expand Down
7 changes: 7 additions & 0 deletions cmd/yorkie/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

const (
DefaultOutput = "" // DefaultOutput is for table format
YamlOutput = "yaml" // YamlOutput is for yaml format
JSONOutput = "json" // JSONOutput is for json format
)
Loading

0 comments on commit d8414e1

Please sign in to comment.