From b6d35424c22bd5515f76622b3f4cbf3e5b4cf451 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Tue, 26 Sep 2023 14:05:38 +0300 Subject: [PATCH] Add support to predefine CLI parameters using config Add support to specify default values for CLI parameters in the config profile, e.g: ``` profiles: - name: default parameter: project-id: ac47e9b2-6d98-48bb-9629-0c790211d20c ``` This allows users to simplify their commands and create profiles for different projects or other resources: `uipath du digitization start --project-id ac47e9b2-6d98-48bb-9629-0c790211d20c --file "my-document.pdf"` can be simplified: `uipath du digitization start --file "my-document.pdf"` --- commandline/command_builder.go | 15 +-- commandline/config_command_handler.go | 15 +-- config/config.go | 11 +- config/config_provider.go | 13 +-- config/profile_yaml.go | 3 +- test/config_set_test.go | 28 +---- test/config_test.go | 159 ++++++++++++++++++++++++-- 7 files changed, 172 insertions(+), 72 deletions(-) diff --git a/commandline/command_builder.go b/commandline/command_builder.go index 9ba49fc..fcb85af 100644 --- a/commandline/command_builder.go +++ b/commandline/command_builder.go @@ -126,13 +126,18 @@ func (b CommandBuilder) createExecutionParameters(context *cli.Context, config * } parameter := executor.NewExecutionParameter(param.FieldName, value, param.In) parameters = append(parameters, *parameter) + } else if configValue, ok := config.Parameter[param.Name]; ok { + value, err := typeConverter.Convert(configValue, param) + if err != nil { + return nil, err + } + parameter := executor.NewExecutionParameter(param.FieldName, value, param.In) + parameters = append(parameters, *parameter) } else if param.Required && param.DefaultValue != nil { parameter := executor.NewExecutionParameter(param.FieldName, param.DefaultValue, param.In) parameters = append(parameters, *parameter) } } - parameters = append(parameters, b.createExecutionParametersFromConfigMap(config.Path, parser.ParameterInPath)...) - parameters = append(parameters, b.createExecutionParametersFromConfigMap(config.Query, parser.ParameterInQuery)...) parameters = append(parameters, b.createExecutionParametersFromConfigMap(config.Header, parser.ParameterInHeader)...) return parameters, nil } @@ -232,11 +237,7 @@ func (b CommandBuilder) getValue(parameter parser.Parameter, context *cli.Contex if value != "" { return value } - value = config.Path[parameter.Name] - if value != "" { - return value - } - value = config.Query[parameter.Name] + value = config.Parameter[parameter.Name] if value != "" { return value } diff --git a/commandline/config_command_handler.go b/commandline/config_command_handler.go index 071fccd..79e808e 100644 --- a/commandline/config_command_handler.go +++ b/commandline/config_command_handler.go @@ -81,11 +81,8 @@ func (h ConfigCommandHandler) setConfigValue(config *config.Config, key string, } else if h.isHeaderKey(keyParts) { config.SetHeader(keyParts[1], value) return nil - } else if h.isPathKey(keyParts) { - config.SetPath(keyParts[1], value) - return nil - } else if h.isQueryKey(keyParts) { - config.SetQuery(keyParts[1], value) + } else if h.isParameterKey(keyParts) { + config.SetParameter(keyParts[1], value) return nil } else if h.isAuthPropertyKey(keyParts) { config.SetAuthProperty(keyParts[2], value) @@ -98,12 +95,8 @@ func (h ConfigCommandHandler) isHeaderKey(keyParts []string) bool { return len(keyParts) == 2 && keyParts[0] == "header" } -func (h ConfigCommandHandler) isPathKey(keyParts []string) bool { - return len(keyParts) == 2 && keyParts[0] == "path" -} - -func (h ConfigCommandHandler) isQueryKey(keyParts []string) bool { - return len(keyParts) == 2 && keyParts[0] == "query" +func (h ConfigCommandHandler) isParameterKey(keyParts []string) bool { + return len(keyParts) == 2 && keyParts[0] == "parameter" } func (h ConfigCommandHandler) isAuthPropertyKey(keyParts []string) bool { diff --git a/config/config.go b/config/config.go index fc6d3ac..9cd655e 100644 --- a/config/config.go +++ b/config/config.go @@ -12,8 +12,7 @@ type Config struct { Uri *url.URL Organization string Tenant string - Path map[string]string - Query map[string]string + Parameter map[string]string Header map[string]string Auth AuthConfig Insecure bool @@ -149,12 +148,8 @@ func (c Config) SetHeader(key string, value string) { c.Header[key] = value } -func (c Config) SetPath(key string, value string) { - c.Path[key] = value -} - -func (c Config) SetQuery(key string, value string) { - c.Query[key] = value +func (c Config) SetParameter(key string, value string) { + c.Parameter[key] = value } func (c Config) SetAuthGrantType(grantType string) { diff --git a/config/config_provider.go b/config/config_provider.go index af5314b..d1dde98 100644 --- a/config/config_provider.go +++ b/config/config_provider.go @@ -46,8 +46,7 @@ func (p *ConfigProvider) Update(profileName string, config Config) error { profile.Tenant = config.Tenant profile.Auth = config.Auth.Config profile.Header = config.Header - profile.Path = config.Path - profile.Query = config.Query + profile.Parameter = config.Parameter profile.Version = config.Version if index == -1 { @@ -67,21 +66,17 @@ func (p ConfigProvider) convertToConfig(profile profileYaml) Config { if profile.Auth == nil { profile.Auth = map[string]interface{}{} } - if profile.Path == nil { - profile.Path = map[string]string{} + if profile.Parameter == nil { + profile.Parameter = map[string]string{} } if profile.Header == nil { profile.Header = map[string]string{} } - if profile.Query == nil { - profile.Query = map[string]string{} - } return Config{ Organization: profile.Organization, Tenant: profile.Tenant, Uri: profile.Uri.URL, - Path: profile.Path, - Query: profile.Query, + Parameter: profile.Parameter, Header: profile.Header, Auth: AuthConfig{ Type: fmt.Sprintf("%v", profile.Auth["type"]), diff --git a/config/profile_yaml.go b/config/profile_yaml.go index c4526b3..79b768a 100644 --- a/config/profile_yaml.go +++ b/config/profile_yaml.go @@ -5,8 +5,7 @@ type profileYaml struct { Organization string `yaml:"organization,omitempty"` Tenant string `yaml:"tenant,omitempty"` Uri urlYaml `yaml:"uri,omitempty"` - Path map[string]string `yaml:"path,omitempty"` - Query map[string]string `yaml:"query,omitempty"` + Parameter map[string]string `yaml:"parameter,omitempty"` Header map[string]string `yaml:"header,omitempty"` Auth map[string]interface{} `yaml:"auth,omitempty"` Insecure bool `yaml:"insecure,omitempty"` diff --git a/test/config_set_test.go b/test/config_set_test.go index c45d3c9..6268330 100644 --- a/test/config_set_test.go +++ b/test/config_set_test.go @@ -231,13 +231,13 @@ func TestConfigSetHeader(t *testing.T) { } } -func TestConfigSetPath(t *testing.T) { +func TestConfigSetParameter(t *testing.T) { configFile := createFile(t) context := NewContextBuilder(). WithConfigFile(configFile). Build() - RunCli([]string{"config", "set", "--key", "path.org", "--value", "my-org"}, context) + RunCli([]string{"config", "set", "--key", "parameter.org", "--value", "my-org"}, context) config, err := os.ReadFile(configFile) if err != nil { @@ -245,7 +245,7 @@ func TestConfigSetPath(t *testing.T) { } expectedConfig := `profiles: - name: default - path: + parameter: org: my-org ` if string(config) != expectedConfig { @@ -253,28 +253,6 @@ func TestConfigSetPath(t *testing.T) { } } -func TestConfigSetQuery(t *testing.T) { - configFile := createFile(t) - context := NewContextBuilder(). - WithConfigFile(configFile). - Build() - - RunCli([]string{"config", "set", "--key", "query.filter", "--value", "my-filter"}, context) - - config, err := os.ReadFile(configFile) - if err != nil { - t.Errorf("Config file does not exist: %v", err) - } - expectedConfig := `profiles: -- name: default - query: - filter: my-filter -` - if string(config) != expectedConfig { - t.Errorf("Expected generated config %v, but got %v", expectedConfig, string(config)) - } -} - func TestConfigSetAuthGrantType(t *testing.T) { configFile := createFile(t) context := NewContextBuilder(). diff --git a/test/config_test.go b/test/config_test.go index d84ffbe..d39ca48 100644 --- a/test/config_test.go +++ b/test/config_test.go @@ -105,11 +105,11 @@ paths: } } -func TestPathConfig(t *testing.T) { +func TestPathParameterConfig(t *testing.T) { config := ` profiles: - name: default - path: + parameter: id: my-id ` definition := ` @@ -140,11 +140,11 @@ paths: } } -func TestQueryConfig(t *testing.T) { +func TestQueryParameterConfig(t *testing.T) { config := ` profiles: - name: default - query: + parameter: filter: my-filter ` definition := ` @@ -152,6 +152,13 @@ paths: /ping: get: summary: Ping operation + parameters: + - name: filter + in: query + required: true + description: The filter + schema: + type: string ` context := NewContextBuilder(). WithDefinition("myservice", definition). @@ -167,18 +174,25 @@ paths: } } -func TestHeaderConfig(t *testing.T) { +func TestHeaderParameterConfig(t *testing.T) { config := ` profiles: - name: default - header: - x-uipath-test: abc + parameter: + x-uipath-test: abc ` definition := ` paths: /ping: get: summary: Ping operation + parameters: + - name: x-uipath-test + in: header + required: true + description: The custom header + schema: + type: string ` context := NewContextBuilder(). WithDefinition("myservice", definition). @@ -195,6 +209,104 @@ paths: } } +func TestCliParameterTakesPrecedenceOverConfigParameter(t *testing.T) { + config := ` +profiles: + - name: default + parameter: + id: my-config-id +` + definition := ` +paths: + /ping/{id}: + get: + summary: Ping operation + operationId: ping + parameters: + - name: id + in: path + required: true + description: The id + schema: + type: string +` + context := NewContextBuilder(). + WithDefinition("myservice", definition). + WithConfig(config). + WithResponse(200, ""). + Build() + + result := RunCli([]string{"myservice", "ping", "--id", "my-id"}, context) + + expected := "my-id" + if !strings.Contains(result.RequestUrl, expected) { + t.Errorf("Did not set correct parameter, expected: %v, got: %v", expected, result.RequestUrl) + } +} + +func TestConfigParameterTakesPrecedenceOverDefaultValue(t *testing.T) { + config := ` +profiles: + - name: default + parameter: + test-filter: my-filter +` + definition := ` +paths: + /ping/{TestFilter}: + get: + summary: Ping operation + operationId: ping + parameters: + - name: TestFilter + in: path + required: true + default: my-default-filter + description: The test filter + schema: + type: string +` + context := NewContextBuilder(). + WithDefinition("myservice", definition). + WithConfig(config). + WithResponse(200, ""). + Build() + + result := RunCli([]string{"myservice", "ping"}, context) + + expected := "my-filter" + if !strings.Contains(result.RequestUrl, expected) { + t.Errorf("Did not set correct parameter, expected: %v, got: %v", expected, result.RequestUrl) + } +} + +func TestConfigParameterNotPassedWhenNotDefined(t *testing.T) { + config := ` +profiles: + - name: default + parameter: + x-uipath-test: abc +` + definition := ` +paths: + /ping: + get: + summary: Ping operation +` + context := NewContextBuilder(). + WithDefinition("myservice", definition). + WithConfig(config). + WithResponse(200, ""). + Build() + + result := RunCli([]string{"myservice", "get-ping"}, context) + + value := result.RequestHeader["x-uipath-test"] + if value != "" { + t.Errorf("Header parameter should not be sent, but got: %v", value) + } +} + func TestCustomProfile(t *testing.T) { config := ` profiles: @@ -256,7 +368,7 @@ func TestRequiredPathParameterFromConfig(t *testing.T) { config := ` profiles: - name: default - path: + parameter: id: abc ` definition := ` @@ -290,7 +402,7 @@ func TestRequiredQueryParameterFromConfig(t *testing.T) { config := ` profiles: - name: default - query: + parameter: id: abc ` definition := ` @@ -324,7 +436,7 @@ func TestRequiredHeaderParameterFromConfig(t *testing.T) { config := ` profiles: - name: default - header: + parameter: x-uipath-test: abc ` definition := ` @@ -353,6 +465,33 @@ paths: } } +func TestAdditionalHeaderParameterFromConfig(t *testing.T) { + config := ` +profiles: + - name: default + header: + x-uipath-test: abc +` + definition := ` +paths: + /ping: + get: + summary: Simple ping +` + context := NewContextBuilder(). + WithDefinition("myservice", definition). + WithConfig(config). + WithResponse(200, ""). + Build() + + result := RunCli([]string{"myservice", "get-ping"}, context) + + header := result.RequestHeader["x-uipath-test"] + if header != "abc" { + t.Errorf("Should send additional header from config, but got %v", header) + } +} + func TestOutputFromConfig(t *testing.T) { config := ` profiles: