Skip to content

Commit

Permalink
feat: output in config (#209)
Browse files Browse the repository at this point in the history
Can read/write output flag to config file

* ensures values are valid
* fix analytics-opt-out in config file to work with multiple definitions of boolean
  • Loading branch information
dbolson authored Apr 25, 2024
1 parent 17b0efb commit e246cbc
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 17 deletions.
11 changes: 8 additions & 3 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package config

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

Expand All @@ -14,6 +13,7 @@ import (
"ldcli/cmd/cliflags"
"ldcli/internal/analytics"
"ldcli/internal/config"
"ldcli/internal/errors"
"ldcli/internal/output"
)

Expand Down Expand Up @@ -76,7 +76,7 @@ func run() func(*cobra.Command, []string) error {
case viper.GetBool(SetFlag):
// flag needs two arguments: a key and value
if len(args)%2 != 0 {
return errors.New("flag needs an argument: --set")
return errors.NewError("flag needs an argument: --set")
}

rawConfig, v, err := getRawConfig()
Expand All @@ -98,7 +98,12 @@ func run() func(*cobra.Command, []string) error {
v.Set(key, value)
}

return writeConfig(config.NewConfig(rawConfig), v, setKeyFn)
configFile, err := config.NewConfig(rawConfig)
if err != nil {
return errors.NewError(err.Error())
}

return writeConfig(configFile, v, setKeyFn)
case viper.IsSet(UnsetFlag):
config, v, err := getConfig()
if err != nil {
Expand Down
10 changes: 3 additions & 7 deletions cmd/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,9 @@ func CmdError(err error, commandPath string) error {
}

func validateOutput(outputFlag string) error {
validKinds := map[string]struct{}{
"json": {},
"plaintext": {},
}
_, ok := validKinds[outputFlag]
if !ok {
return output.ErrInvalidOutputKind
_, err := output.NewOutputKind(outputFlag)
if err != nil {
return err
}

return nil
Expand Down
31 changes: 24 additions & 7 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/mitchellh/go-homedir"

"ldcli/cmd/cliflags"
"ldcli/internal/errors"
"ldcli/internal/output"
)

const Filename = ".ldcli-config.yml"
Expand All @@ -16,28 +18,43 @@ type ConfigFile struct {
AccessToken string `json:"access-token,omitempty" yaml:"access-token,omitempty"`
AnalyticsOptOut *bool `json:"analytics-opt-out,omitempty" yaml:"analytics-opt-out,omitempty"`
BaseURI string `json:"base-uri,omitempty" yaml:"base-uri,omitempty"`
Output string `json:"output,omitempty" yaml:"output,omitempty"`
}

func NewConfig(rawConfig map[string]interface{}) ConfigFile {
var accessToken string
func NewConfig(rawConfig map[string]interface{}) (ConfigFile, error) {
var (
accessToken string
analyticsOptOut bool
baseURI string
err error
outputKind output.OutputKind
)
if rawConfig[cliflags.AccessTokenFlag] != nil {
accessToken = rawConfig[cliflags.AccessTokenFlag].(string)
}
var analyticsOptOut bool
if rawConfig[cliflags.AnalyticsOptOut] != nil {
stringValue := rawConfig[cliflags.AnalyticsOptOut].(string)
analyticsOptOut, _ = strconv.ParseBool(stringValue)
stringValue := fmt.Sprintf("%v", rawConfig[cliflags.AnalyticsOptOut])
analyticsOptOut, err = strconv.ParseBool(stringValue)
if err != nil {
return ConfigFile{}, errors.NewError("analytics-opt-out must be true or false")
}
}
var baseURI string
if rawConfig[cliflags.BaseURIFlag] != nil {
baseURI = rawConfig[cliflags.BaseURIFlag].(string)
}
if rawConfig[cliflags.OutputFlag] != nil {
outputKind, err = output.NewOutputKind(rawConfig[cliflags.OutputFlag].(string))
if err != nil {
return ConfigFile{}, err
}
}

return ConfigFile{
AccessToken: accessToken,
AnalyticsOptOut: &analyticsOptOut,
BaseURI: baseURI,
}
Output: outputKind.String(),
}, nil
}

func GetConfigPath() string {
Expand Down
95 changes: 95 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package config_test

import (
"fmt"
"ldcli/internal/config"
"testing"

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

func TestNewConfig(t *testing.T) {
t.Run("analytics-opt-out", func(t *testing.T) {
tests := map[string]struct {
expected bool
input interface{}
}{
"when value is true": {
expected: true,
input: true,
},
"when value is 1": {
expected: true,
input: 1,
},
"when value is false": {
expected: false,
input: false,
},
"when value is 0": {
expected: false,
input: 0,
},
}
for name, tt := range tests {
tt := tt
t.Run(fmt.Sprintf("%s is %t", name, tt.expected), func(t *testing.T) {
rawConfig := map[string]interface{}{
"analytics-opt-out": tt.input,
}

configFile, err := config.NewConfig(rawConfig)

require.NoError(t, err)
assert.Equal(t, tt.expected, *configFile.AnalyticsOptOut)
})
}
})

t.Run("is an error when analytics-opt-out is something else", func(t *testing.T) {
rawConfig := map[string]interface{}{
"analytics-opt-out": "something",
}

_, err := config.NewConfig(rawConfig)

assert.EqualError(t, err, "analytics-opt-out must be true or false")
})

t.Run("analytics-opt-out", func(t *testing.T) {
tests := map[string]struct {
input string
}{
"is valid when value is json": {
input: "json",
},
"is valid when value is plaintext": {
input: "json",
},
}
for name, tt := range tests {
tt := tt
t.Run(name, func(t *testing.T) {
rawConfig := map[string]interface{}{
"output": tt.input,
}

configFile, err := config.NewConfig(rawConfig)

require.NoError(t, err)
assert.Equal(t, tt.input, configFile.Output)
})
}
})

t.Run("is invalid with an invalid output", func(t *testing.T) {
rawConfig := map[string]interface{}{
"output": "invalid",
}

_, err := config.NewConfig(rawConfig)

assert.EqualError(t, err, "output is invalid")
})
}
24 changes: 24 additions & 0 deletions internal/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@ import (

var ErrInvalidOutputKind = errors.NewError("output is invalid")

type OutputKind string

func (o OutputKind) String() string {
return string(o)
}

var (
OutputKindJSON = OutputKind("json")
OutputKindNull = OutputKind("")
OutputKindPlaintext = OutputKind("plaintext")
)

func NewOutputKind(s string) (OutputKind, error) {
validKinds := map[string]struct{}{
OutputKindJSON.String(): {},
OutputKindPlaintext.String(): {},
}
if _, isValid := validKinds[s]; !isValid {
return OutputKindNull, ErrInvalidOutputKind
}

return OutputKind(s), nil
}

// Outputter defines the different ways a command's response can be formatted based on
// user input.
type Outputter interface {
Expand Down

0 comments on commit e246cbc

Please sign in to comment.