From b5a0d10fc6ebd39cbb3e98c3798fef9572b64e38 Mon Sep 17 00:00:00 2001 From: Chris Trombley Date: Mon, 12 Jul 2021 09:52:50 -0700 Subject: [PATCH] refactor: add SetValueFunc and case fold region values --- internal/config/api/api.go | 2 +- internal/config/command/command.go | 2 +- internal/config/config.go | 5 +++-- internal/config/json_store.go | 30 ++++++++++++++++++++++++++++++ internal/config/json_store_test.go | 23 +++++++++++++++++++++++ 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/internal/config/api/api.go b/internal/config/api/api.go index b8e278ac2..7b40974cc 100644 --- a/internal/config/api/api.go +++ b/internal/config/api/api.go @@ -225,7 +225,7 @@ func SetConfigValue(key config.FieldKey, value interface{}) error { } // DeleteConfigValue deletes a config value for the given key. -func DeleteConfigValue(key config.FieldKey, value interface{}) error { +func DeleteConfigValue(key config.FieldKey) error { return config.ConfigStore.DeleteKey(key) } diff --git a/internal/config/command/command.go b/internal/config/command/command.go index 83e38590b..f3cd29edf 100644 --- a/internal/config/command/command.go +++ b/internal/config/command/command.go @@ -38,7 +38,7 @@ The set command sets a persistent configuration value for the New Relic CLI. log.Fatalf("%s is not a valid config field. valid values are %s", key, configAPI.GetValidConfigFieldKeys()) } - if err := configAPI.DeleteConfigValue(config.FieldKey(key), value); err != nil { + if err := configAPI.SetConfigValue(config.FieldKey(key), value); err != nil { log.Fatal(err) } diff --git a/internal/config/config.go b/internal/config/config.go index c88013045..95630b958 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,7 +19,7 @@ const ( LogLevel FieldKey = "loglevel" PluginDir FieldKey = "plugindir" PreReleaseFeatures FieldKey = "prereleasefeatures" - SendUsageData FieldKey = "sendusagedata" + SendUsageData FieldKey = "sendUsageData" DefaultProfileName = "default" @@ -73,7 +73,8 @@ func InitializeCredentialsStore() { region.US.String(), region.EU.String(), ), - Default: region.US.String(), + Default: region.US.String(), + SetValueFunc: ToLower(), }, FieldDefinition{ Key: AccountID, diff --git a/internal/config/json_store.go b/internal/config/json_store.go index a229edf80..f976ab9d3 100644 --- a/internal/config/json_store.go +++ b/internal/config/json_store.go @@ -53,12 +53,21 @@ type FieldDefinition struct { // is performed for the underlying value. If the func returns an error, the // set operation will not succeed. SetValidationFunc FieldValueValidationFunc + + // SetValueFunc is a translation func that is run when a set operation + // is performed for the underlying value. The value provided will be run + // through the func provided and the resulting value will be set. + SetValueFunc FieldValueTranslationFunc } // FieldValueValidationFunc is a configurable validation func that will ensure a field // value conforms to some constraints before being set. type FieldValueValidationFunc func(key FieldKey, value interface{}) error +// FieldValueTranslationFunc is a configurable translation func that will modify +// a value before setting it in the underlying config instance. +type FieldValueTranslationFunc func(key FieldKey, value interface{}) (interface{}, error) + // IntGreaterThan is a FieldValueValidationFunc ins a validation func that ensures // the field value is an integer greater than 0. func IntGreaterThan(greaterThan int) func(key FieldKey, value interface{}) error { @@ -117,6 +126,18 @@ func StringInStrings(caseSensitive bool, allowedValues ...string) func(key Field } } +// ToLower is a FieldValueTranslationFunc translation func that ensures the provided +// value is case-folded to lowercase before writing to the underlying config. +func ToLower() func(key FieldKey, value interface{}) (interface{}, error) { + return func(key FieldKey, value interface{}) (interface{}, error) { + if s, ok := value.(string); ok { + return strings.ToLower(s), nil + } + + return nil, fmt.Errorf("the value %s provided for %s is not a string", value, key) + } +} + // JSONStoreOption is a func for supplying options when creating a new JSONStore. type JSONStoreOption func(*JSONStore) error @@ -391,6 +412,15 @@ func (p *JSONStore) SetWithScope(scope string, key FieldKey, value interface{}) // use the case convention from the field definition key = v.Key } + + if v.SetValueFunc != nil { + var err error + value, err = v.SetValueFunc(key, value) + if err != nil { + return err + } + } + } else if p.explicitValues { return fmt.Errorf("key '%s' is not valid, valid keys are: %v", key, p.getConfigValueKeys()) } diff --git a/internal/config/json_store_test.go b/internal/config/json_store_test.go index 588ce3d4d..8f66213a5 100644 --- a/internal/config/json_store_test.go +++ b/internal/config/json_store_test.go @@ -591,3 +591,26 @@ func TestStore_Set_ValidationFunc_StringInStrings_WrongType(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "is not a string") } + +func TestStore_Set_ValueFunc_ToLower(t *testing.T) { + f, err := ioutil.TempFile("", "newrelic-cli.config_provider_test.*.json") + require.NoError(t, err) + defer f.Close() + + p, err := NewJSONStore( + ConfigureFields(FieldDefinition{ + Key: "testString", + SetValueFunc: ToLower(), + }), + PersistToFile(f.Name()), + ) + require.NoError(t, err) + + err = p.Set("testString", "TEST_VALUE") + require.NoError(t, err) + + v, err := p.GetString("testString") + require.NoError(t, err) + + require.Equal(t, "test_value", v) +}