From 23c6de5565967944e18456040057d0d8185c4365 Mon Sep 17 00:00:00 2001 From: Nick Pocock Date: Thu, 21 Jul 2022 08:37:38 +0200 Subject: [PATCH] Task: Get config cmd (#3552) * Task: Return error from keep alive methods within httputil pkg * Initial commit for get config task * Add unit tests * Remove output package, change case default to last statement within switch, improve test setup * modify test * Add jsonString method * make json string unexported * Remove message struct and return endpoint data directly * Add wrapped erorr * use ExactValidArgs() Co-authored-by: dustinxie Co-authored-by: huof6890 <68298506@qq.com> --- ioctl/config/config.go | 2 + ioctl/newcmd/config/config.go | 63 +++++++++++++++++++++----- ioctl/newcmd/config/config_get.go | 50 ++++++++++++++++++++ ioctl/newcmd/config/config_get_test.go | 43 ++++++++++++++++++ ioctl/newcmd/config/config_test.go | 61 +++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 ioctl/newcmd/config/config_get_test.go diff --git a/ioctl/config/config.go b/ioctl/config/config.go index d1e3ba2bde..a16c0f8206 100644 --- a/ioctl/config/config.go +++ b/ioctl/config/config.go @@ -33,6 +33,8 @@ var ( ErrConfigNotMatch = fmt.Errorf("no matching config") // ErrEmptyEndpoint indicates error for empty endpoint ErrEmptyEndpoint = fmt.Errorf("no endpoint has been set") + // ErrConfigDefaultAccountNotSet indicates an error for the default account not being set + ErrConfigDefaultAccountNotSet = fmt.Errorf("default account not set") ) // Language type used to enumerate supported language of ioctl diff --git a/ioctl/newcmd/config/config.go b/ioctl/newcmd/config/config.go index b702847558..7d3e982dbe 100644 --- a/ioctl/newcmd/config/config.go +++ b/ioctl/newcmd/config/config.go @@ -7,9 +7,9 @@ package config import ( + "encoding/json" "fmt" "os" - "path" "path/filepath" "regexp" "strconv" @@ -95,7 +95,7 @@ func InitConfig() (config.Config, string, error) { } } // Set language for ioctl - if info.isSupportedLanguage(info.readConfig.Language) == -1 { + if isSupportedLanguage(info.readConfig.Language) == -1 { fmt.Printf("Warn: Language %s is not supported, English instead.\n", info.readConfig.Language) } return info.readConfig, info.defaultConfigFile, nil @@ -111,7 +111,7 @@ func newInfo(readConfig config.Config, defaultConfigFile string) *info { // reset resets all values of config func (c *info) reset() error { - c.readConfig.Wallet = path.Dir(c.defaultConfigFile) + c.readConfig.Wallet = filepath.Dir(c.defaultConfigFile) c.readConfig.Endpoint = "" c.readConfig.SecureConnect = true c.readConfig.DefaultAccount = *new(config.Context) @@ -128,17 +128,34 @@ func (c *info) reset() error { return nil } -// isSupportedLanguage checks if the language is a supported option and returns index when supported -func (c *info) isSupportedLanguage(arg string) config.Language { - if index, err := strconv.Atoi(arg); err == nil && index >= 0 && index < len(_supportedLanguage) { - return config.Language(index) - } - for i, lang := range _supportedLanguage { - if strings.EqualFold(arg, lang) { - return config.Language(i) +// get retrieves a config item from its key. +func (c *info) get(arg string) (string, error) { + switch arg { + case "endpoint": + if c.readConfig.Endpoint == "" { + return "", config.ErrEmptyEndpoint } + return fmt.Sprintf("%s secure connect(TLS): %t", c.readConfig.Endpoint, c.readConfig.SecureConnect), nil + case "wallet": + return c.readConfig.Wallet, nil + case "defaultacc": + if c.readConfig.DefaultAccount.AddressOrAlias == "" { + return "", config.ErrConfigDefaultAccountNotSet + } + return jsonString(c.readConfig.DefaultAccount) + case "explorer": + return c.readConfig.Explorer, nil + case "language": + return c.readConfig.Language, nil + case "nsv2height": + return strconv.FormatUint(c.readConfig.Nsv2height, 10), nil + case "analyserEndpoint": + return c.readConfig.AnalyserEndpoint, nil + case "all": + return jsonString(c.readConfig) + default: + return "", config.ErrConfigNotMatch } - return config.Language(-1) } // writeConfig writes to config file @@ -164,3 +181,25 @@ func (c *info) loadConfig() error { } return nil } + +// isSupportedLanguage checks if the language is a supported option and returns index when supported +func isSupportedLanguage(arg string) config.Language { + if index, err := strconv.Atoi(arg); err == nil && index >= 0 && index < len(_supportedLanguage) { + return config.Language(index) + } + for i, lang := range _supportedLanguage { + if strings.EqualFold(arg, lang) { + return config.Language(i) + } + } + return config.Language(-1) +} + +// jsonString returns json string for message +func jsonString(input interface{}) (string, error) { + byteAsJSON, err := json.MarshalIndent(input, "", " ") + if err != nil { + return "", errors.Wrap(err, "failed to JSON marshal config field") + } + return fmt.Sprint(string(byteAsJSON)), nil +} diff --git a/ioctl/newcmd/config/config_get.go b/ioctl/newcmd/config/config_get.go index 96afdd8d01..9182947200 100644 --- a/ioctl/newcmd/config/config_get.go +++ b/ioctl/newcmd/config/config_get.go @@ -5,3 +5,53 @@ // License 2.0 that can be found in the LICENSE file. package config + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/iotexproject/iotex-core/ioctl" + "github.com/iotexproject/iotex-core/ioctl/config" +) + +var ( + _configGetUse = map[config.Language]string{ + config.English: "get VARIABLE", + config.Chinese: "get 变量", + } + _configGetUseCmdShorts = map[config.Language]string{ + config.English: "Get config fields from ioctl", + config.Chinese: "从 ioctl 获取配置字段", + } + _configGetUseCmdLong = map[config.Language]string{ + config.English: "Get config fields from ioctl\nValid Variables: [" + strings.Join(_validGetArgs, ", ") + "]", + config.Chinese: "从 ioctl 获取配置字段\n有效变量: [" + strings.Join(_validGetArgs, ", ") + "]", + } +) + +// NewConfigGetCmd is a command to get config fields from iotcl. +func NewConfigGetCmd(client ioctl.Client) *cobra.Command { + use, _ := client.SelectTranslation(_configGetUse) + short, _ := client.SelectTranslation(_configGetUseCmdShorts) + long, _ := client.SelectTranslation(_configGetUseCmdLong) + + return &cobra.Command{ + Use: use, + Short: short, + Long: long, + ValidArgs: _validGetArgs, + Args: cobra.ExactValidArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + result, err := newInfo(client.Config(), client.ConfigFilePath()).get(args[0]) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("issue fetching config value %s", args[0])) + } + cmd.Println(result) + return nil + }, + } +} diff --git a/ioctl/newcmd/config/config_get_test.go b/ioctl/newcmd/config/config_get_test.go new file mode 100644 index 0000000000..7ffa693f5a --- /dev/null +++ b/ioctl/newcmd/config/config_get_test.go @@ -0,0 +1,43 @@ +package config + +import ( + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/iotexproject/iotex-core/ioctl/config" + "github.com/iotexproject/iotex-core/ioctl/util" + "github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient" +) + +func TestNewConfigGetCmd(t *testing.T) { + require := require.New(t) + ctrl := gomock.NewController(t) + defer ctrl.Finish() + client := mock_ioctlclient.NewMockClient(ctrl) + client.EXPECT().Config().Return(config.Config{Endpoint: "test"}).AnyTimes() + client.EXPECT().SelectTranslation(gomock.Any()).Return("config reset", config.English).AnyTimes() + + t.Run("get config value", func(t *testing.T) { + client.EXPECT().ConfigFilePath().Return(fmt.Sprintf("%s/%s", t.TempDir(), "config.file")) + cmd := NewConfigGetCmd(client) + result, err := util.ExecuteCmd(cmd, "endpoint") + require.NoError(err) + require.Contains(result, "test") + }) + + t.Run("get unknown config value", func(t *testing.T) { + cmd := NewConfigGetCmd(client) + _, err := util.ExecuteCmd(cmd, "random-args") + require.Contains(err.Error(), "invalid argument \"random-args\"") + }) + + t.Run("config value error", func(t *testing.T) { + client.EXPECT().ConfigFilePath().Return(fmt.Sprintf("%s/%s", t.TempDir(), "config.file")) + cmd := NewConfigGetCmd(client) + _, err := util.ExecuteCmd(cmd, "defaultacc") + require.Contains(err.Error(), "issue fetching config value defaultacc: default account not set") + }) +} diff --git a/ioctl/newcmd/config/config_test.go b/ioctl/newcmd/config/config_test.go index 240bbb1a3e..4a1445e74a 100644 --- a/ioctl/newcmd/config/config_test.go +++ b/ioctl/newcmd/config/config_test.go @@ -28,6 +28,67 @@ func TestInitConfig(t *testing.T) { require.Equal(filepath.Join(testPath, _defaultConfigFileName), cfgFilePath) } +func TestConfigGet(t *testing.T) { + require := require.New(t) + testPath := t.TempDir() + info := newInfo(config.Config{ + Wallet: testPath, + SecureConnect: true, + Aliases: make(map[string]string), + DefaultAccount: config.Context{AddressOrAlias: "test"}, + Explorer: "iotexscan", + Language: "English", + AnalyserEndpoint: "testAnalyser", + }, testPath) + + tcs := []struct { + arg string + expected string + }{ + { + "endpoint", + "no endpoint has been set", + }, + { + "wallet", + testPath, + }, + { + "defaultacc", + "{\n \"addressOrAlias\": \"test\"\n}", + }, + { + "explorer", + "iotexscan", + }, + { + "language", + "English", + }, + { + "nsv2height", + "0", + }, + { + "analyserEndpoint", + "testAnalyser", + }, + { + "all", + "\"endpoint\": \"\",\n \"secureConnect\": true,\n \"aliases\": {},\n \"defaultAccount\": {\n \"addressOrAlias\": \"test\"\n },\n \"explorer\": \"iotexscan\",\n \"language\": \"English\",\n \"nsv2height\": 0,\n \"analyserEndpoint\": \"testAnalyser\"\n}", + }, + } + + for _, tc := range tcs { + cfgItem, err := info.get(tc.arg) + if err != nil { + require.Contains(err.Error(), tc.expected) + } else { + require.Contains(cfgItem, tc.expected) + } + } +} + func TestConfigReset(t *testing.T) { require := require.New(t) cfgDir := t.TempDir()