mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 03:58:01 +00:00
Refactor formatting of output
This change is almost perfectly compatible with the existing code, except it's a little shorter because it uses a list of a available formatters that must implement a `command.Formatter` interface. Also added some basic formatting tests.
This commit is contained in:
@@ -3,6 +3,7 @@ package command
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -15,86 +16,102 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func OutputSecret(ui cli.Ui, format string, secret *api.Secret) int {
|
func OutputSecret(ui cli.Ui, format string, secret *api.Secret) int {
|
||||||
switch format {
|
return outputWithFormat(ui, format, secret)
|
||||||
case "json":
|
|
||||||
return outputFormatJSON(ui, secret)
|
|
||||||
case "yaml":
|
|
||||||
return outputFormatYAML(ui, secret)
|
|
||||||
case "table":
|
|
||||||
return outputFormatTable(ui, secret, true)
|
|
||||||
default:
|
|
||||||
ui.Error(fmt.Sprintf("Invalid output format: %s", format))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func OutputList(ui cli.Ui, format string, secret *api.Secret) int {
|
func OutputList(ui cli.Ui, format string, secret *api.Secret) int {
|
||||||
switch format {
|
return outputWithFormat(ui, format, secret.Data["keys"])
|
||||||
case "json":
|
}
|
||||||
return outputFormatJSONList(ui, secret)
|
|
||||||
case "yaml":
|
func outputWithFormat(ui cli.Ui, format string, data interface{}) int {
|
||||||
return outputFormatYAMLList(ui, secret)
|
formatter, ok := Formatters[strings.ToLower(format)]
|
||||||
case "table":
|
if !ok {
|
||||||
return outputFormatTableList(ui, secret)
|
|
||||||
default:
|
|
||||||
ui.Error(fmt.Sprintf("Invalid output format: %s", format))
|
ui.Error(fmt.Sprintf("Invalid output format: %s", format))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
if err := formatter.Output(ui, data); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Could not output secret: %s", err.Error()))
|
||||||
func outputFormatJSON(ui cli.Ui, s *api.Secret) int {
|
|
||||||
b, err := json.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error formatting secret: %s", err))
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Formatter interface {
|
||||||
|
Output(ui cli.Ui, data interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var Formatters = map[string]Formatter{
|
||||||
|
"json": JsonFormatter{},
|
||||||
|
"table": TableFormatter{},
|
||||||
|
"yaml": YamlFormatter{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// An output formatter for json output of an object
|
||||||
|
type JsonFormatter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j JsonFormatter) Output(ui cli.Ui, data interface{}) error {
|
||||||
|
b, err := json.Marshal(data)
|
||||||
|
if err == nil {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
json.Indent(&out, b, "", "\t")
|
json.Indent(&out, b, "", "\t")
|
||||||
ui.Output(out.String())
|
ui.Output(out.String())
|
||||||
return 0
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputFormatJSONList(ui cli.Ui, s *api.Secret) int {
|
// An output formatter for yaml output format of an object
|
||||||
b, err := json.Marshal(s.Data["keys"])
|
type YamlFormatter struct {
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error formatting keys: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
var out bytes.Buffer
|
|
||||||
json.Indent(&out, b, "", "\t")
|
|
||||||
ui.Output(out.String())
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func outputFormatYAML(ui cli.Ui, s *api.Secret) int {
|
|
||||||
b, err := yaml.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error formatting secret: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (y YamlFormatter) Output(ui cli.Ui, data interface{}) error {
|
||||||
|
b, err := yaml.Marshal(data)
|
||||||
|
if err == nil {
|
||||||
ui.Output(strings.TrimSpace(string(b)))
|
ui.Output(strings.TrimSpace(string(b)))
|
||||||
return 0
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputFormatYAMLList(ui cli.Ui, s *api.Secret) int {
|
// An output formatter for table output of an object
|
||||||
b, err := yaml.Marshal(s.Data["keys"])
|
type TableFormatter struct {
|
||||||
if err != nil {
|
|
||||||
ui.Error(fmt.Sprintf(
|
|
||||||
"Error formatting secret: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Output(strings.TrimSpace(string(b)))
|
func (t TableFormatter) Output(ui cli.Ui, data interface{}) error {
|
||||||
return 0
|
// TODO: this should really use reflection like the other formatters do
|
||||||
|
if s, ok := data.(*api.Secret); ok {
|
||||||
|
return t.OutputSecret(ui, s)
|
||||||
|
}
|
||||||
|
if s, ok := data.([]interface{}); ok {
|
||||||
|
return t.OutputList(ui, s)
|
||||||
|
}
|
||||||
|
return errors.New("Cannot use the table formatter for this type")
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputFormatTable(ui cli.Ui, s *api.Secret, whitespace bool) int {
|
func (t TableFormatter) OutputList(ui cli.Ui, list []interface{}) error {
|
||||||
|
config := columnize.DefaultConfig()
|
||||||
|
config.Delim = "♨"
|
||||||
|
config.Glue = "\t"
|
||||||
|
config.Prefix = ""
|
||||||
|
|
||||||
|
input := make([]string, 0, 5)
|
||||||
|
|
||||||
|
input = append(input, "Keys")
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(list))
|
||||||
|
for _, k := range list {
|
||||||
|
keys = append(keys, k.(string))
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
input = append(input, fmt.Sprintf("%s", k))
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Output(columnize.Format(input, config))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TableFormatter) OutputSecret(ui cli.Ui, s *api.Secret) error {
|
||||||
config := columnize.DefaultConfig()
|
config := columnize.DefaultConfig()
|
||||||
config.Delim = "♨"
|
config.Delim = "♨"
|
||||||
config.Glue = "\t"
|
config.Glue = "\t"
|
||||||
@@ -145,36 +162,5 @@ func outputFormatTable(ui cli.Ui, s *api.Secret, whitespace bool) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui.Output(columnize.Format(input, config))
|
ui.Output(columnize.Format(input, config))
|
||||||
return 0
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func outputFormatTableList(ui cli.Ui, s *api.Secret) int {
|
|
||||||
config := columnize.DefaultConfig()
|
|
||||||
config.Delim = "♨"
|
|
||||||
config.Glue = "\t"
|
|
||||||
config.Prefix = ""
|
|
||||||
|
|
||||||
input := make([]string, 0, 5)
|
|
||||||
|
|
||||||
input = append(input, "Keys")
|
|
||||||
|
|
||||||
keys := make([]string, 0, len(s.Data["keys"].([]interface{})))
|
|
||||||
for _, k := range s.Data["keys"].([]interface{}) {
|
|
||||||
keys = append(keys, k.(string))
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
for _, k := range keys {
|
|
||||||
input = append(input, fmt.Sprintf("%s", k))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.Warnings) != 0 {
|
|
||||||
input = append(input, "")
|
|
||||||
for _, warning := range s.Warnings {
|
|
||||||
input = append(input, fmt.Sprintf("* %s", warning))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Output(columnize.Format(input, config))
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|||||||
81
command/format_test.go
Normal file
81
command/format_test.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
"github.com/hashicorp/vault/api"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var output string
|
||||||
|
|
||||||
|
type mockUi struct {
|
||||||
|
t *testing.T
|
||||||
|
SampleData string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockUi) Ask(_ string) (string, error) {
|
||||||
|
m.t.FailNow()
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
func (m mockUi) AskSecret(_ string) (string, error) {
|
||||||
|
m.t.FailNow()
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
func (m mockUi) Output(s string) {
|
||||||
|
output = s
|
||||||
|
}
|
||||||
|
func (m mockUi) Info(s string) {
|
||||||
|
m.t.Log(s)
|
||||||
|
}
|
||||||
|
func (m mockUi) Error(s string) {
|
||||||
|
m.t.Log(s)
|
||||||
|
}
|
||||||
|
func (m mockUi) Warn(s string) {
|
||||||
|
m.t.Log(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonFormatter(t *testing.T) {
|
||||||
|
ui := mockUi{t: t, SampleData: "something"}
|
||||||
|
if err := outputWithFormat(ui, "json", ui); err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var newUi mockUi
|
||||||
|
if err := json.Unmarshal([]byte(output), &newUi); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if newUi.SampleData != ui.SampleData {
|
||||||
|
t.Fatalf(`values not equal ("%s" != "%s")`,
|
||||||
|
newUi.SampleData,
|
||||||
|
ui.SampleData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestYamlFormatter(t *testing.T) {
|
||||||
|
ui := mockUi{t: t, SampleData: "something"}
|
||||||
|
if err := outputWithFormat(ui, "yaml", ui); err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var newUi mockUi
|
||||||
|
err := yaml.Unmarshal([]byte(output), &newUi)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if newUi.SampleData != ui.SampleData {
|
||||||
|
t.Fatalf(`values not equal ("%s" != "%s")`,
|
||||||
|
newUi.SampleData,
|
||||||
|
ui.SampleData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTableFormatter(t *testing.T) {
|
||||||
|
ui := mockUi{t: t}
|
||||||
|
s := api.Secret{Data: map[string]interface{}{"k": "something"}}
|
||||||
|
if err := outputWithFormat(ui, "table", &s); err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "something") {
|
||||||
|
t.Fatal("did not find something")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user