From 243cd7e8d956d2bed5a9ff0ff6647a4869262389 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Tue, 1 Oct 2024 12:18:43 +0300 Subject: [PATCH] Create command abstraction and split up command builder Added command and flag definition structures to separate building the command metadata and actually rendering the CLI commands. Moved all the interaction with the cli/v2 module in the cli.go source file which simplifies the interactive with the module and abstracts the details away. The change also moves out some parts from the command_builder which grew in complexity. --- commandline/autocomplete_handler.go | 43 +- commandline/cli.go | 135 +++++- commandline/command_builder.go | 712 ++++++++++------------------ commandline/command_definition.go | 33 ++ commandline/flag_builder.go | 92 +++- commandline/flag_definition.go | 25 + commandline/flag_type.go | 26 + commandline/show_command_handler.go | 38 +- 8 files changed, 561 insertions(+), 543 deletions(-) create mode 100644 commandline/command_definition.go create mode 100644 commandline/flag_definition.go create mode 100644 commandline/flag_type.go diff --git a/commandline/autocomplete_handler.go b/commandline/autocomplete_handler.go index 953971b..cf558f0 100644 --- a/commandline/autocomplete_handler.go +++ b/commandline/autocomplete_handler.go @@ -6,16 +6,14 @@ import ( "os" "path/filepath" "strings" - - "github.com/urfave/cli/v2" ) +const AutocompletePowershell = "powershell" +const AutocompleteBash = "bash" + const directoryPermissions = 0755 const filePermissions = 0644 -const Powershell = "powershell" -const Bash = "bash" - const completeHandlerEnabledCheck = "uipath_auto_complete" const powershellCompleteHandler = ` @@ -55,8 +53,8 @@ type autoCompleteHandler struct { } func (a autoCompleteHandler) EnableCompleter(shell string, filePath string) (string, error) { - if shell != Powershell && shell != Bash { - return "", fmt.Errorf("Invalid shell, supported values: %s, %s", Powershell, Bash) + if shell != AutocompletePowershell && shell != AutocompleteBash { + return "", fmt.Errorf("Invalid shell, supported values: %s, %s", AutocompletePowershell, AutocompleteBash) } profileFilePath, err := a.profileFilePath(shell, filePath) @@ -71,14 +69,14 @@ func (a autoCompleteHandler) profileFilePath(shell string, filePath string) (str if filePath != "" { return filePath, nil } - if shell == Powershell { + if shell == AutocompletePowershell { return PowershellProfilePath() } return BashrcPath() } func (a autoCompleteHandler) completeHandler(shell string) string { - if shell == Powershell { + if shell == AutocompletePowershell { return powershellCompleteHandler } return bashCompleteHandler @@ -136,17 +134,12 @@ func (a autoCompleteHandler) writeCompleterHandler(filePath string, completerHan return nil } -func (a autoCompleteHandler) Find(commandText string, commands []*cli.Command, exclude []string) []string { +func (a autoCompleteHandler) Find(commandText string, command *commandDefinition, exclude []string) []string { words := strings.Split(commandText, " ") if len(words) < 2 { return []string{} } - command := &cli.Command{ - Name: "uipath", - Subcommands: commands, - } - for _, word := range words[1 : len(words)-1] { if strings.HasPrefix(word, "-") { break @@ -164,7 +157,7 @@ func (a autoCompleteHandler) Find(commandText string, commands []*cli.Command, e return a.searchCommands(lastWord, command.Subcommands, exclude) } -func (a autoCompleteHandler) findCommand(name string, commands []*cli.Command) *cli.Command { +func (a autoCompleteHandler) findCommand(name string, commands []*commandDefinition) *commandDefinition { for _, command := range commands { if command.Name == name { return command @@ -173,7 +166,7 @@ func (a autoCompleteHandler) findCommand(name string, commands []*cli.Command) * return nil } -func (a autoCompleteHandler) searchCommands(word string, commands []*cli.Command, exclude []string) []string { +func (a autoCompleteHandler) searchCommands(word string, commands []*commandDefinition, exclude []string) []string { result := []string{} for _, command := range commands { if strings.HasPrefix(command.Name, word) { @@ -188,22 +181,16 @@ func (a autoCompleteHandler) searchCommands(word string, commands []*cli.Command return a.removeDuplicates(a.removeExcluded(result, exclude)) } -func (a autoCompleteHandler) searchFlags(word string, command *cli.Command, exclude []string) []string { +func (a autoCompleteHandler) searchFlags(word string, command *commandDefinition, exclude []string) []string { result := []string{} for _, flag := range command.Flags { - flagNames := flag.Names() - for _, flagName := range flagNames { - if strings.HasPrefix(flagName, word) { - result = append(result, "--"+flagName) - } + if strings.HasPrefix(flag.Name, word) { + result = append(result, "--"+flag.Name) } } for _, flag := range command.Flags { - flagNames := flag.Names() - for _, flagName := range flagNames { - if strings.Contains(flagName, word) { - result = append(result, "--"+flagName) - } + if strings.Contains(flag.Name, word) { + result = append(result, "--"+flag.Name) } } return a.removeDuplicates(a.removeExcluded(result, exclude)) diff --git a/commandline/cli.go b/commandline/cli.go index 018d45b..22dbe2b 100644 --- a/commandline/cli.go +++ b/commandline/cli.go @@ -40,7 +40,11 @@ func (c Cli) run(args []string, input utils.Stream) error { PluginExecutor: c.pluginExecutor, DefinitionProvider: c.definitionProvider, } - flags := CommandBuilder.CreateDefaultFlags(false) + + flagBuilder := newFlagBuilder() + flagBuilder.AddDefaultFlags(false) + flags := flagBuilder.ToList() + commands, err := CommandBuilder.Create(args) if err != nil { return err @@ -51,8 +55,8 @@ func (c Cli) run(args []string, input utils.Stream) error { Usage: "Command-Line Interface for UiPath Services", UsageText: "uipath --parameter", Version: "1.0", - Flags: flags, - Commands: commands, + Flags: c.convertFlags(flags...), + Commands: c.convertCommands(commands...), Writer: c.stdOut, ErrWriter: c.stdErr, HideVersion: true, @@ -89,3 +93,128 @@ func NewCli( ) *Cli { return &Cli{stdIn, stdOut, stdErr, colors, definitionProvider, configProvider, executor, pluginExecutor} } + +func (c Cli) convertCommand(command *commandDefinition) *cli.Command { + result := cli.Command{ + Name: command.Name, + Usage: command.Summary, + Description: command.Description, + Flags: c.convertFlags(command.Flags...), + Subcommands: c.convertCommands(command.Subcommands...), + CustomHelpTemplate: command.CustomHelpTemplate, + Hidden: command.Hidden, + HideHelp: true, + } + if command.Action != nil { + result.Action = func(context *cli.Context) error { + return command.Action(&CommandExecContext{context}) + } + } + return &result +} + +func (c Cli) convertCommands(commands ...*commandDefinition) []*cli.Command { + result := []*cli.Command{} + for _, command := range commands { + result = append(result, c.convertCommand(command)) + } + return result +} + +func (c Cli) convertStringSliceFlag(flag *flagDefinition) *cli.StringSliceFlag { + envVars := []string{} + if flag.EnvVarName != "" { + envVars = append(envVars, flag.EnvVarName) + } + var value *cli.StringSlice + if flag.Value != nil { + value = cli.NewStringSlice(flag.Value.([]string)...) + } + return &cli.StringSliceFlag{ + Name: flag.Name, + Usage: flag.Summary, + EnvVars: envVars, + Required: flag.Required, + Hidden: flag.Hidden, + Value: value, + } +} + +func (c Cli) convertIntFlag(flag *flagDefinition) *cli.IntFlag { + envVars := []string{} + if flag.EnvVarName != "" { + envVars = append(envVars, flag.EnvVarName) + } + var value int + if flag.Value != nil { + value = flag.Value.(int) + } + return &cli.IntFlag{ + Name: flag.Name, + Usage: flag.Summary, + EnvVars: envVars, + Required: flag.Required, + Hidden: flag.Hidden, + Value: value, + } +} + +func (c Cli) convertBoolFlag(flag *flagDefinition) *cli.BoolFlag { + envVars := []string{} + if flag.EnvVarName != "" { + envVars = append(envVars, flag.EnvVarName) + } + var value bool + if flag.Value != nil { + value = flag.Value.(bool) + } + return &cli.BoolFlag{ + Name: flag.Name, + Usage: flag.Summary, + EnvVars: envVars, + Required: flag.Required, + Hidden: flag.Hidden, + Value: value, + } +} + +func (c Cli) convertStringFlag(flag *flagDefinition) *cli.StringFlag { + envVars := []string{} + if flag.EnvVarName != "" { + envVars = append(envVars, flag.EnvVarName) + } + var value string + if flag.Value != nil { + value = flag.Value.(string) + } + return &cli.StringFlag{ + Name: flag.Name, + Usage: flag.Summary, + EnvVars: envVars, + Required: flag.Required, + Hidden: flag.Hidden, + Value: value, + } +} + +func (c Cli) convertFlag(flag *flagDefinition) cli.Flag { + switch flag.Type { + case FlagTypeStringArray: + return c.convertStringSliceFlag(flag) + case FlagTypeInteger: + return c.convertIntFlag(flag) + case FlagTypeBoolean: + return c.convertBoolFlag(flag) + case FlagTypeString: + return c.convertStringFlag(flag) + } + panic(fmt.Sprintf("Unknown flag type: %s", flag.Type.String())) +} + +func (c Cli) convertFlags(flags ...*flagDefinition) []cli.Flag { + result := []cli.Flag{} + for _, flag := range flags { + result = append(result, c.convertFlag(flag)) + } + return result +} diff --git a/commandline/command_builder.go b/commandline/command_builder.go index 9bbd149..4128025 100644 --- a/commandline/command_builder.go +++ b/commandline/command_builder.go @@ -17,46 +17,8 @@ import ( "github.com/UiPath/uipathcli/output" "github.com/UiPath/uipathcli/parser" "github.com/UiPath/uipathcli/utils" - "github.com/urfave/cli/v2" ) -const FromStdIn = "-" - -const insecureFlagName = "insecure" -const debugFlagName = "debug" -const profileFlagName = "profile" -const uriFlagName = "uri" -const identityUriFlagName = "identity-uri" -const organizationFlagName = "organization" -const tenantFlagName = "tenant" -const helpFlagName = "help" -const outputFormatFlagName = "output" -const queryFlagName = "query" -const waitFlagName = "wait" -const waitTimeoutFlagName = "wait-timeout" -const versionFlagName = "version" -const fileFlagName = "file" - -var predefinedFlags = []string{ - insecureFlagName, - debugFlagName, - profileFlagName, - uriFlagName, - identityUriFlagName, - organizationFlagName, - tenantFlagName, - helpFlagName, - outputFormatFlagName, - queryFlagName, - waitFlagName, - waitTimeoutFlagName, - versionFlagName, - fileFlagName, -} - -const outputFormatJson = "json" -const outputFormatText = "text" - const subcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} @@ -87,29 +49,29 @@ type CommandBuilder struct { DefinitionProvider DefinitionProvider } -func (b CommandBuilder) sort(commands []*cli.Command) { +func (b CommandBuilder) sort(commands []*commandDefinition) { sort.Slice(commands, func(i, j int) bool { return commands[i].Name < commands[j].Name }) } -func (b CommandBuilder) fileInput(context *cli.Context, parameters []parser.Parameter) utils.Stream { - value := context.String(fileFlagName) +func (b CommandBuilder) fileInput(context *CommandExecContext, parameters []parser.Parameter) utils.Stream { + value := context.String(FlagNameFile) if value == "" { return nil } - if value == FromStdIn { + if value == FlagValueFromStdIn { return b.Input } for _, param := range parameters { - if strings.EqualFold(param.FieldName, fileFlagName) { + if strings.EqualFold(param.FieldName, FlagNameFile) { return nil } } return utils.NewFileStream(value) } -func (b CommandBuilder) createExecutionParameters(context *cli.Context, config *config.Config, operation parser.Operation) (executor.ExecutionParameters, error) { +func (b CommandBuilder) createExecutionParameters(context *CommandExecContext, config *config.Config, operation parser.Operation) (executor.ExecutionParameters, error) { typeConverter := newTypeConverter() parameters := []executor.ExecutionParameter{} @@ -163,23 +125,16 @@ func (b CommandBuilder) formatAllowedValues(values []interface{}) string { return result } -func (b CommandBuilder) createFlags(parameters []parser.Parameter) []cli.Flag { - flags := []cli.Flag{} +func (b CommandBuilder) createFlags(parameters []parser.Parameter) []*flagDefinition { + flags := []*flagDefinition{} for _, parameter := range parameters { formatter := newParameterFormatter(parameter) + flagType := FlagTypeString if parameter.IsArray() { - flag := cli.StringSliceFlag{ - Name: parameter.Name, - Usage: formatter.Description(), - } - flags = append(flags, &flag) - } else { - flag := cli.StringFlag{ - Name: parameter.Name, - Usage: formatter.Description(), - } - flags = append(flags, &flag) + flagType = FlagTypeStringArray } + flag := newFlagDefinition(parameter.Name, formatter.Description(), "", flagType, "", nil, false, false) + flags = append(flags, flag) } return flags } @@ -196,21 +151,21 @@ func (b CommandBuilder) sortParameters(parameters []parser.Parameter) { }) } -func (b CommandBuilder) outputFormat(config config.Config, context *cli.Context) (string, error) { - outputFormat := context.String(outputFormatFlagName) +func (b CommandBuilder) outputFormat(config config.Config, context *CommandExecContext) (string, error) { + outputFormat := context.String(FlagNameOutputFormat) if outputFormat == "" { outputFormat = config.Output } if outputFormat == "" { - outputFormat = outputFormatJson + outputFormat = FlagValueOutputFormatJson } - if outputFormat != outputFormatJson && outputFormat != outputFormatText { - return "", fmt.Errorf("Invalid output format '%s', allowed values: %s, %s", outputFormat, outputFormatJson, outputFormatText) + if outputFormat != FlagValueOutputFormatJson && outputFormat != FlagValueOutputFormatText { + return "", fmt.Errorf("Invalid output format '%s', allowed values: %s, %s", outputFormat, FlagValueOutputFormatJson, FlagValueOutputFormatText) } return outputFormat, nil } -func (b CommandBuilder) createBaseUri(operation parser.Operation, config config.Config, context *cli.Context) (url.URL, error) { +func (b CommandBuilder) createBaseUri(operation parser.Operation, config config.Config, context *CommandExecContext) (url.URL, error) { uriArgument, err := b.parseUriArgument(context) if err != nil { return operation.BaseUri, err @@ -222,12 +177,12 @@ func (b CommandBuilder) createBaseUri(operation parser.Operation, config config. return builder.Uri(), nil } -func (b CommandBuilder) createIdentityUri(context *cli.Context, config config.Config, baseUri url.URL) (*url.URL, error) { - uri := context.String(identityUriFlagName) +func (b CommandBuilder) createIdentityUri(context *CommandExecContext, config config.Config, baseUri url.URL) (*url.URL, error) { + uri := context.String(FlagNameIdentityUri) if uri != "" { identityUri, err := url.Parse(uri) if err != nil { - return nil, fmt.Errorf("Error parsing %s argument: %w", identityUriFlagName, err) + return nil, fmt.Errorf("Error parsing %s argument: %w", FlagNameIdentityUri, err) } return identityUri, nil } @@ -248,19 +203,19 @@ func (b CommandBuilder) createIdentityUri(context *cli.Context, config config.Co return identityUri, nil } -func (b CommandBuilder) parseUriArgument(context *cli.Context) (*url.URL, error) { - uriFlag := context.String(uriFlagName) +func (b CommandBuilder) parseUriArgument(context *CommandExecContext) (*url.URL, error) { + uriFlag := context.String(FlagNameUri) if uriFlag == "" { return nil, nil } uriArgument, err := url.Parse(uriFlag) if err != nil { - return nil, fmt.Errorf("Error parsing %s argument: %w", uriFlagName, err) + return nil, fmt.Errorf("Error parsing %s argument: %w", FlagNameUri, err) } return uriArgument, nil } -func (b CommandBuilder) getValue(parameter parser.Parameter, context *cli.Context, config config.Config) string { +func (b CommandBuilder) getValue(parameter parser.Parameter, context *CommandExecContext, config config.Config) string { value := context.String(parameter.Name) if value != "" { return value @@ -279,7 +234,7 @@ func (b CommandBuilder) getValue(parameter parser.Parameter, context *cli.Contex return "" } -func (b CommandBuilder) validateArguments(context *cli.Context, parameters []parser.Parameter, config config.Config) error { +func (b CommandBuilder) validateArguments(context *CommandExecContext, parameters []parser.Parameter, config config.Config) error { err := errors.New("Invalid arguments:") result := true for _, parameter := range parameters { @@ -321,7 +276,7 @@ func (b CommandBuilder) outputWriter(writer io.Writer, format string, query stri if query != "" { transformer = output.NewJmesPathTransformer(query) } - if format == outputFormatText { + if format == FlagValueOutputFormatText { return output.NewTextOutputWriter(writer, transformer) } return output.NewJsonOutputWriter(writer, transformer) @@ -334,91 +289,82 @@ func (b CommandBuilder) executeCommand(context executor.ExecutionContext, writer return b.Executor.Call(context, writer, logger) } -func (b CommandBuilder) createOperationCommand(operation parser.Operation) *cli.Command { +func (b CommandBuilder) createOperationCommand(operation parser.Operation) *commandDefinition { parameters := operation.Parameters b.sortParameters(parameters) flagBuilder := newFlagBuilder() flagBuilder.AddFlags(b.createFlags(parameters)) - flagBuilder.AddFlags(b.CreateDefaultFlags(true)) - flagBuilder.AddFlag(b.HelpFlag()) - - return &cli.Command{ - Name: operation.Name, - Usage: operation.Summary, - Description: operation.Description, - Flags: flagBuilder.ToList(), - CustomHelpTemplate: subcommandHelpTemplate, - Action: func(context *cli.Context) error { - profileName := context.String(profileFlagName) - config := b.ConfigProvider.Config(profileName) - if config == nil { - return fmt.Errorf("Could not find profile '%s'", profileName) - } - outputFormat, err := b.outputFormat(*config, context) - if err != nil { - return err - } - query := context.String(queryFlagName) - wait := context.String(waitFlagName) - waitTimeout := context.Int(waitTimeoutFlagName) - - baseUri, err := b.createBaseUri(operation, *config, context) - if err != nil { - return err - } + flagBuilder.AddDefaultFlags(true) + flagBuilder.AddHelpFlag() + + return newCommandDefinition(operation.Name, operation.Summary, operation.Description, flagBuilder.ToList(), nil, subcommandHelpTemplate, operation.Hidden, func(context *CommandExecContext) error { + profileName := context.String(FlagNameProfile) + config := b.ConfigProvider.Config(profileName) + if config == nil { + return fmt.Errorf("Could not find profile '%s'", profileName) + } + outputFormat, err := b.outputFormat(*config, context) + if err != nil { + return err + } + query := context.String(FlagNameQuery) + wait := context.String(FlagNameWait) + waitTimeout := context.Int(FlagNameWaitTimeout) - input := b.fileInput(context, operation.Parameters) - if input == nil { - err = b.validateArguments(context, operation.Parameters, *config) - if err != nil { - return err - } - } + baseUri, err := b.createBaseUri(operation, *config, context) + if err != nil { + return err + } - parameters, err := b.createExecutionParameters(context, config, operation) + input := b.fileInput(context, operation.Parameters) + if input == nil { + err = b.validateArguments(context, operation.Parameters, *config) if err != nil { return err } + } - organization := context.String(organizationFlagName) - if organization == "" { - organization = config.Organization - } - tenant := context.String(tenantFlagName) - if tenant == "" { - tenant = config.Tenant - } - insecure := context.Bool(insecureFlagName) || config.Insecure - debug := context.Bool(debugFlagName) || config.Debug - identityUri, err := b.createIdentityUri(context, *config, baseUri) - if err != nil { - return err - } + parameters, err := b.createExecutionParameters(context, config, operation) + if err != nil { + return err + } - executionContext := executor.NewExecutionContext( - organization, - tenant, - operation.Method, - baseUri, - operation.Route, - operation.ContentType, - input, - parameters, - config.Auth, - insecure, - debug, - *identityUri, - operation.Plugin) - - if wait != "" { - return b.executeWait(*executionContext, outputFormat, query, wait, waitTimeout) - } - return b.execute(*executionContext, outputFormat, query, nil) - }, - HideHelp: true, - Hidden: operation.Hidden, - } + organization := context.String(FlagNameOrganization) + if organization == "" { + organization = config.Organization + } + tenant := context.String(FlagNameTenant) + if tenant == "" { + tenant = config.Tenant + } + insecure := context.Bool(FlagNameInsecure) || config.Insecure + debug := context.Bool(FlagNameDebug) || config.Debug + identityUri, err := b.createIdentityUri(context, *config, baseUri) + if err != nil { + return err + } + + executionContext := executor.NewExecutionContext( + organization, + tenant, + operation.Method, + baseUri, + operation.Route, + operation.ContentType, + input, + parameters, + config.Auth, + insecure, + debug, + *identityUri, + operation.Plugin) + + if wait != "" { + return b.executeWait(*executionContext, outputFormat, query, wait, waitTimeout) + } + return b.execute(*executionContext, outputFormat, query, nil) + }) } func (b CommandBuilder) executeWait(executionContext executor.ExecutionContext, outputFormat string, query string, wait string, waitTimeout int) error { @@ -495,20 +441,15 @@ func (b CommandBuilder) execute(executionContext executor.ExecutionContext, outp return err } -func (b CommandBuilder) createCategoryCommand(operation parser.Operation) *cli.Command { - return &cli.Command{ - Name: operation.Category.Name, - Usage: operation.Category.Summary, - Description: operation.Category.Description, - Flags: []cli.Flag{ - b.HelpFlag(), - b.VersionFlag(true), - }, - HideHelp: true, - } +func (b CommandBuilder) createCategoryCommand(operation parser.Operation) *commandDefinition { + flagBuilder := newFlagBuilder() + flagBuilder.AddHelpFlag() + flagBuilder.AddVersionFlag(true) + + return newCommandDefinition(operation.Category.Name, operation.Category.Summary, operation.Category.Description, flagBuilder.ToList(), nil, "", false, nil) } -func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, categories map[string]*cli.Command) (bool, *cli.Command) { +func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, categories map[string]*commandDefinition) (bool, *commandDefinition) { isNewCategory := false operationCommand := b.createOperationCommand(operation) command, found := categories[operation.Category.Name] @@ -521,9 +462,9 @@ func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, return isNewCategory, command } -func (b CommandBuilder) createServiceCommand(definition parser.Definition) *cli.Command { - categories := map[string]*cli.Command{} - commands := []*cli.Command{} +func (b CommandBuilder) createServiceCommand(definition parser.Definition) *commandDefinition { + categories := map[string]*commandDefinition{} + commands := []*commandDefinition{} for _, operation := range definition.Operations { if operation.Category == nil { command := b.createOperationCommand(operation) @@ -540,178 +481,113 @@ func (b CommandBuilder) createServiceCommand(definition parser.Definition) *cli. b.sort(command.Subcommands) } - return &cli.Command{ - Name: definition.Name, - Usage: definition.Summary, - Description: definition.Description, - Flags: []cli.Flag{ - b.HelpFlag(), - b.VersionFlag(true), - }, - Subcommands: commands, - HideHelp: true, - } + flagBuilder := newFlagBuilder() + flagBuilder.AddHelpFlag() + flagBuilder.AddVersionFlag(true) + + return newCommandDefinition(definition.Name, definition.Summary, definition.Description, flagBuilder.ToList(), commands, "", false, nil) } -func (b CommandBuilder) createAutoCompleteEnableCommand() *cli.Command { +func (b CommandBuilder) createAutoCompleteEnableCommand() *commandDefinition { const shellFlagName = "shell" - const powershellFlagValue = "powershell" - const bashFlagValue = "bash" const fileFlagName = "file" - return &cli.Command{ - Name: "enable", - Usage: "Enable auto complete", - Description: "Enables auto complete in your shell", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: shellFlagName, - Usage: fmt.Sprintf("%s, %s", powershellFlagValue, bashFlagValue), - Required: true, - }, - &cli.StringFlag{ - Name: fileFlagName, - Hidden: true, - }, - b.HelpFlag(), - }, - Action: func(context *cli.Context) error { - shell := context.String(shellFlagName) - filePath := context.String(fileFlagName) - handler := newAutoCompleteHandler() - output, err := handler.EnableCompleter(shell, filePath) - if err != nil { - return err - } - fmt.Fprintln(b.StdOut, output) - return nil - }, - HideHelp: true, - } -} - -func (b CommandBuilder) createAutoCompleteCompleteCommand(version string) *cli.Command { - return &cli.Command{ - Name: "complete", - Usage: "Autocomplete suggestions", - Description: "Returns the autocomplete suggestions", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "command", - Usage: "The command to autocomplete", - Required: true, - }, - b.HelpFlag(), - }, - Action: func(context *cli.Context) error { - commandText := context.String("command") - exclude := []string{} - for _, flagName := range predefinedFlags { - exclude = append(exclude, "--"+flagName) - } - args := strings.Split(commandText, " ") - definitions, err := b.loadAutocompleteDefinitions(args, version) - if err != nil { - return err - } - commands := b.createServiceCommands(definitions) - handler := newAutoCompleteHandler() - words := handler.Find(commandText, commands, exclude) - for _, word := range words { - fmt.Fprintln(b.StdOut, word) - } - return nil - }, - HideHelp: true, - } -} - -func (b CommandBuilder) createAutoCompleteCommand(version string) *cli.Command { - return &cli.Command{ - Name: "autocomplete", - Usage: "Autocompletion", - Description: "Commands for autocompletion", - Flags: []cli.Flag{ - b.HelpFlag(), - }, - Subcommands: []*cli.Command{ - b.createAutoCompleteEnableCommand(), - b.createAutoCompleteCompleteCommand(version), - }, - HideHelp: true, - } -} - -func (b CommandBuilder) createConfigCommand() *cli.Command { - authFlagName := "auth" - flags := []cli.Flag{ - &cli.StringFlag{ - Name: authFlagName, - Usage: fmt.Sprintf("Authorization type: %s, %s, %s", CredentialsAuth, LoginAuth, PatAuth), - }, - &cli.StringFlag{ - Name: profileFlagName, - Usage: "Profile to configure", - EnvVars: []string{"UIPATH_PROFILE"}, - Value: config.DefaultProfile, - }, - b.HelpFlag(), - } - - return &cli.Command{ - Name: "config", - Usage: "Interactive Configuration", - Description: "Interactive command to configure the CLI", - Flags: flags, - Subcommands: []*cli.Command{ - b.createConfigSetCommand(), - }, - Action: func(context *cli.Context) error { - auth := context.String(authFlagName) - profileName := context.String(profileFlagName) - handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider) - return handler.Configure(auth, profileName) - }, - HideHelp: true, - } -} - -func (b CommandBuilder) createConfigSetCommand() *cli.Command { - keyFlagName := "key" - valueFlagName := "value" - flags := []cli.Flag{ - &cli.StringFlag{ - Name: keyFlagName, - Usage: "The key", - Required: true, - }, - &cli.StringFlag{ - Name: valueFlagName, - Usage: "The value to set", - Required: true, - }, - &cli.StringFlag{ - Name: profileFlagName, - Usage: "Profile to configure", - EnvVars: []string{"UIPATH_PROFILE"}, - Value: config.DefaultProfile, - }, - b.HelpFlag(), - } - return &cli.Command{ - Name: "set", - Usage: "Set config parameters", - Description: "Set config parameters", - Flags: flags, - Action: func(context *cli.Context) error { - profileName := context.String(profileFlagName) - key := context.String(keyFlagName) - value := context.String(valueFlagName) - handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider) - return handler.Set(key, value, profileName) - }, - HideHelp: true, + flagBuilder := newFlagBuilder() + flagBuilder.AddFlag(newFlagDefinition(shellFlagName, fmt.Sprintf("%s, %s", AutocompletePowershell, AutocompleteBash), "", FlagTypeString, "", nil, false, true)) + flagBuilder.AddFlag(newFlagDefinition(fileFlagName, "", "", FlagTypeString, "", nil, true, false)) + flagBuilder.AddHelpFlag() + + return newCommandDefinition("enable", "Enable auto complete", "Enables auto complete in your shell", flagBuilder.ToList(), nil, "", false, func(context *CommandExecContext) error { + shell := context.String(shellFlagName) + filePath := context.String(fileFlagName) + handler := newAutoCompleteHandler() + output, err := handler.EnableCompleter(shell, filePath) + if err != nil { + return err + } + fmt.Fprintln(b.StdOut, output) + return nil + }) +} + +func (b CommandBuilder) createAutoCompleteCompleteCommand(version string) *commandDefinition { + const commandFlagName = "command" + + flagBuilder := newFlagBuilder() + flagBuilder.AddFlag(newFlagDefinition(commandFlagName, "The command to autocomplete", "", FlagTypeString, "", nil, false, true)) + flagBuilder.AddHelpFlag() + + return newCommandDefinition("complete", "Autocomplete suggestions", "Returns the autocomplete suggestions", flagBuilder.ToList(), nil, "", false, func(context *CommandExecContext) error { + commandText := context.String("command") + exclude := []string{} + for _, flagName := range FlagNamesPredefined { + exclude = append(exclude, "--"+flagName) + } + args := strings.Split(commandText, " ") + definitions, err := b.loadAutocompleteDefinitions(args, version) + if err != nil { + return err + } + commands := b.createServiceCommands(definitions) + command := newCommandDefinition("uipath", "", "", nil, commands, "", false, nil) + handler := newAutoCompleteHandler() + words := handler.Find(commandText, command, exclude) + for _, word := range words { + fmt.Fprintln(b.StdOut, word) + } + return nil + }) +} + +func (b CommandBuilder) createAutoCompleteCommand(version string) *commandDefinition { + flagBuilder := newFlagBuilder() + flagBuilder.AddHelpFlag() + + subcommands := []*commandDefinition{ + b.createAutoCompleteEnableCommand(), + b.createAutoCompleteCompleteCommand(version), + } + + return newCommandDefinition("autocomplete", "Autocompletion", "Commands for autocompletion", flagBuilder.ToList(), subcommands, "", false, nil) +} + +func (b CommandBuilder) createConfigCommand() *commandDefinition { + const flagNameAuth = "auth" + + flagBuilder := newFlagBuilder() + flagBuilder.AddFlag(newFlagDefinition(flagNameAuth, fmt.Sprintf("Authorization type: %s, %s, %s", CredentialsAuth, LoginAuth, PatAuth), "", FlagTypeString, "", nil, false, false)) + flagBuilder.AddFlag(newFlagDefinition(FlagNameProfile, "Profile to configure", "", FlagTypeString, "UIPATH_PROFILE", config.DefaultProfile, false, false)) + flagBuilder.AddHelpFlag() + + subcommands := []*commandDefinition{ + b.createConfigSetCommand(), } + + return newCommandDefinition("config", "Interactive Configuration", "Interactive command to configure the CLI", flagBuilder.ToList(), subcommands, "", false, func(context *CommandExecContext) error { + auth := context.String(flagNameAuth) + profileName := context.String(FlagNameProfile) + handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider) + return handler.Configure(auth, profileName) + }) +} + +func (b CommandBuilder) createConfigSetCommand() *commandDefinition { + const flagNameKey = "key" + const flagNameValue = "value" + + flagBuilder := newFlagBuilder() + flagBuilder.AddFlag(newFlagDefinition(flagNameKey, "The key", "", FlagTypeString, "", nil, false, true)) + flagBuilder.AddFlag(newFlagDefinition(flagNameValue, "The value to set", "", FlagTypeString, "", nil, false, true)) + flagBuilder.AddFlag(newFlagDefinition(FlagNameProfile, "Profile to configure", "", FlagTypeString, "UIPATH_PROFILE", config.DefaultProfile, false, false)) + flagBuilder.AddHelpFlag() + + return newCommandDefinition("set", "Set config parameters", "Set config parameters", flagBuilder.ToList(), nil, "", false, func(context *CommandExecContext) error { + profileName := context.String(FlagNameProfile) + key := context.String(flagNameKey) + value := context.String(flagNameValue) + handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider) + return handler.Set(key, value, profileName) + }) } func (b CommandBuilder) loadDefinitions(args []string, version string) ([]parser.Definition, error) { @@ -753,47 +629,38 @@ func (b CommandBuilder) loadAutocompleteDefinitions(args []string, version strin return b.loadDefinitions(args, version) } -func (b CommandBuilder) createShowCommand(definitions []parser.Definition) *cli.Command { - return &cli.Command{ - Name: "commands", - Usage: "Inspect available CLI operations", - Description: "Command to inspect available uipath CLI operations", - Flags: []cli.Flag{ - b.HelpFlag(), - }, - Subcommands: []*cli.Command{ - { - Name: "show", - Usage: "Print CLI commands", - Description: "Print available uipath CLI commands", - Flags: []cli.Flag{ - b.HelpFlag(), - }, - Action: func(context *cli.Context) error { - flagBuilder := newFlagBuilder() - flagBuilder.AddFlags(b.CreateDefaultFlags(false)) - flagBuilder.AddFlag(b.HelpFlag()) - flags := flagBuilder.ToList() - - handler := newShowCommandHandler() - output, err := handler.Execute(definitions, flags) - if err != nil { - return err - } - fmt.Fprintln(b.StdOut, output) - return nil - }, - HideHelp: true, - Hidden: true, - }, - }, - HideHelp: true, - Hidden: true, - } -} - -func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*cli.Command { - commands := []*cli.Command{} +func (b CommandBuilder) createShowCommand(definitions []parser.Definition) *commandDefinition { + flagBuilder := newFlagBuilder() + flagBuilder.AddHelpFlag() + + return newCommandDefinition("show", "Print CLI commands", "Print available uipath CLI commands", flagBuilder.ToList(), nil, "", true, func(context *CommandExecContext) error { + builder := newFlagBuilder() + builder.AddDefaultFlags(false) + builder.AddHelpFlag() + + handler := newShowCommandHandler() + output, err := handler.Execute(definitions, builder.ToList()) + if err != nil { + return err + } + fmt.Fprintln(b.StdOut, output) + return nil + }) +} + +func (b CommandBuilder) createInspectCommand(definitions []parser.Definition) *commandDefinition { + flagBuilder := newFlagBuilder() + flagBuilder.AddHelpFlag() + + subcommands := []*commandDefinition{ + b.createShowCommand(definitions), + } + + return newCommandDefinition("commands", "Inspect available CLI operations", "Command to inspect available uipath CLI operations", flagBuilder.ToList(), subcommands, "", true, nil) +} + +func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*commandDefinition { + commands := []*commandDefinition{} for _, e := range definitions { command := b.createServiceCommand(e) commands = append(commands, command) @@ -820,9 +687,9 @@ func (b CommandBuilder) versionFromProfile(profile string) string { return config.Version } -func (b CommandBuilder) Create(args []string) ([]*cli.Command, error) { - version := b.parseArgument(args, versionFlagName) - profile := b.parseArgument(args, profileFlagName) +func (b CommandBuilder) Create(args []string) ([]*commandDefinition, error) { + version := b.parseArgument(args, FlagNameVersion) + profile := b.parseArgument(args, FlagNameProfile) if version == "" && profile != "" { version = b.versionFromProfile(profile) } @@ -833,108 +700,7 @@ func (b CommandBuilder) Create(args []string) ([]*cli.Command, error) { servicesCommands := b.createServiceCommands(definitions) autocompleteCommand := b.createAutoCompleteCommand(version) configCommand := b.createConfigCommand() - showCommand := b.createShowCommand(definitions) - commands := append(servicesCommands, autocompleteCommand, configCommand, showCommand) + inspectCommand := b.createInspectCommand(definitions) + commands := append(servicesCommands, autocompleteCommand, configCommand, inspectCommand) return commands, nil } - -func (b CommandBuilder) CreateDefaultFlags(hidden bool) []cli.Flag { - return []cli.Flag{ - &cli.BoolFlag{ - Name: debugFlagName, - Usage: "Enable debug output", - EnvVars: []string{"UIPATH_DEBUG"}, - Value: false, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: profileFlagName, - Usage: "Config profile to use", - EnvVars: []string{"UIPATH_PROFILE"}, - Value: config.DefaultProfile, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: uriFlagName, - Usage: "Server Base-URI", - EnvVars: []string{"UIPATH_URI"}, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: organizationFlagName, - Usage: "Organization name", - EnvVars: []string{"UIPATH_ORGANIZATION"}, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: tenantFlagName, - Usage: "Tenant name", - EnvVars: []string{"UIPATH_TENANT"}, - Hidden: hidden, - }, - &cli.BoolFlag{ - Name: insecureFlagName, - Usage: "Disable HTTPS certificate check", - EnvVars: []string{"UIPATH_INSECURE"}, - Value: false, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: outputFormatFlagName, - Usage: fmt.Sprintf("Set output format: %s (default), %s", outputFormatJson, outputFormatText), - EnvVars: []string{"UIPATH_OUTPUT"}, - Value: "", - Hidden: hidden, - }, - &cli.StringFlag{ - Name: queryFlagName, - Usage: "Perform JMESPath query on output", - Value: "", - Hidden: hidden, - }, - &cli.StringFlag{ - Name: waitFlagName, - Usage: "Waits for the provided condition (JMESPath expression)", - Value: "", - Hidden: hidden, - }, - &cli.IntFlag{ - Name: waitTimeoutFlagName, - Usage: "Time to wait in seconds for condition", - Value: 30, - Hidden: hidden, - }, - &cli.StringFlag{ - Name: fileFlagName, - Usage: "Provide input from file (use - for stdin)", - Value: "", - Hidden: hidden, - }, - &cli.StringFlag{ - Name: identityUriFlagName, - Usage: "Identity Server URI", - EnvVars: []string{"UIPATH_IDENTITY_URI"}, - Hidden: hidden, - }, - b.VersionFlag(hidden), - } -} - -func (b CommandBuilder) VersionFlag(hidden bool) cli.Flag { - return &cli.StringFlag{ - Name: versionFlagName, - Usage: "Specific service version", - EnvVars: []string{"UIPATH_VERSION"}, - Value: "", - Hidden: hidden, - } -} - -func (b CommandBuilder) HelpFlag() cli.Flag { - return &cli.BoolFlag{ - Name: helpFlagName, - Usage: "Show help", - Value: false, - Hidden: true, - } -} diff --git a/commandline/command_definition.go b/commandline/command_definition.go new file mode 100644 index 0000000..c9c6f6b --- /dev/null +++ b/commandline/command_definition.go @@ -0,0 +1,33 @@ +package commandline + +import "github.com/urfave/cli/v2" + +type CommandExecContext struct { + *cli.Context +} + +type CommandExecFunc func(*CommandExecContext) error + +type commandDefinition struct { + Name string + Summary string + Description string + Flags []*flagDefinition + Subcommands []*commandDefinition + CustomHelpTemplate string + Hidden bool + Action CommandExecFunc +} + +func newCommandDefinition(name string, summary string, description string, flags []*flagDefinition, subcommands []*commandDefinition, customHelpTemplate string, hidden bool, action CommandExecFunc) *commandDefinition { + return &commandDefinition{ + name, + summary, + description, + flags, + subcommands, + customHelpTemplate, + hidden, + action, + } +} diff --git a/commandline/flag_builder.go b/commandline/flag_builder.go index bca3eb3..2342675 100644 --- a/commandline/flag_builder.go +++ b/commandline/flag_builder.go @@ -1,36 +1,112 @@ package commandline import ( - "github.com/urfave/cli/v2" + "fmt" + + "github.com/UiPath/uipathcli/config" ) +const FlagNameDebug = "debug" +const FlagNameProfile = "profile" +const FlagNameUri = "uri" +const FlagNameOrganization = "organization" +const FlagNameTenant = "tenant" +const FlagNameInsecure = "insecure" +const FlagNameOutputFormat = "output" +const FlagNameQuery = "query" +const FlagNameWait = "wait" +const FlagNameWaitTimeout = "wait-timeout" +const FlagNameFile = "file" +const FlagNameIdentityUri = "identity-uri" +const FlagNameVersion = "version" +const FlagNameHelp = "help" + +const FlagValueFromStdIn = "-" +const FlagValueOutputFormatJson = "json" +const FlagValueOutputFormatText = "text" + +var FlagNamesPredefined = []string{ + FlagNameDebug, + FlagNameProfile, + FlagNameUri, + FlagNameOrganization, + FlagNameTenant, + FlagNameInsecure, + FlagNameOutputFormat, + FlagNameQuery, + FlagNameWait, + FlagNameWaitTimeout, + FlagNameFile, + FlagNameIdentityUri, + FlagNameVersion, + FlagNameHelp, +} + type flagBuilder struct { - flags map[string]cli.Flag + flags map[string]*flagDefinition order []string } -func (b *flagBuilder) AddFlag(flag cli.Flag) { - name := flag.Names()[0] +func (b *flagBuilder) AddFlag(flag *flagDefinition) { + name := flag.Name if _, found := b.flags[name]; !found { b.flags[name] = flag b.order = append(b.order, name) } } -func (b *flagBuilder) AddFlags(flags []cli.Flag) { +func (b *flagBuilder) AddFlags(flags []*flagDefinition) { for _, flag := range flags { b.AddFlag(flag) } } -func (b flagBuilder) ToList() []cli.Flag { - flags := []cli.Flag{} +func (b *flagBuilder) AddDefaultFlags(hidden bool) { + b.AddFlags(b.defaultFlags(hidden)) +} + +func (b *flagBuilder) AddHelpFlag() { + b.AddFlag(b.helpFlag()) +} + +func (b *flagBuilder) AddVersionFlag(hidden bool) { + b.AddFlag(b.versionFlag(hidden)) +} + +func (b flagBuilder) ToList() []*flagDefinition { + flags := []*flagDefinition{} for _, name := range b.order { flags = append(flags, b.flags[name]) } return flags } +func (b flagBuilder) defaultFlags(hidden bool) []*flagDefinition { + return []*flagDefinition{ + newFlagDefinition(FlagNameDebug, "Enable debug output", "", FlagTypeBoolean, "UIPATH_DEBUG", false, hidden, false), + newFlagDefinition(FlagNameProfile, "Config profile to use", "", FlagTypeString, "UIPATH_PROFILE", config.DefaultProfile, hidden, false), + newFlagDefinition(FlagNameUri, "Server Base-URI", "", FlagTypeString, "UIPATH_URI", "", hidden, false), + newFlagDefinition(FlagNameOrganization, "Organization name", "", FlagTypeString, "UIPATH_ORGANIZATION", nil, hidden, false), + newFlagDefinition(FlagNameTenant, "Tenant name", "", FlagTypeString, "UIPATH_TENANT", "", hidden, false), + newFlagDefinition(FlagNameInsecure, "Disable HTTPS certificate check", "", FlagTypeBoolean, "UIPATH_INSECURE", false, hidden, false), + newFlagDefinition(FlagNameOutputFormat, fmt.Sprintf("Set output format: %s (default), %s", FlagValueOutputFormatJson, FlagValueOutputFormatText), "", FlagTypeString, "UIPATH_OUTPUT", "", hidden, false), + newFlagDefinition(FlagNameQuery, "Perform JMESPath query on output", "", FlagTypeString, "", "", hidden, false), + newFlagDefinition(FlagNameWait, "Waits for the provided condition (JMESPath expression)", "", FlagTypeString, "", "", hidden, false), + newFlagDefinition(FlagNameWaitTimeout, "Time to wait in seconds for condition", "", FlagTypeInteger, "", 30, hidden, false), + newFlagDefinition(FlagNameFile, "Provide input from file (use - for stdin)", "", FlagTypeString, "", "", hidden, false), + newFlagDefinition(FlagNameIdentityUri, "Identity Server URI", "", FlagTypeString, "UIPATH_IDENTITY_URI", "", hidden, false), + b.versionFlag(hidden), + } +} + +func (b flagBuilder) versionFlag(hidden bool) *flagDefinition { + return newFlagDefinition(FlagNameVersion, "Specific service version", "", FlagTypeString, "UIPATH_VERSION", "", hidden, false) +} + +func (b flagBuilder) helpFlag() *flagDefinition { + return newFlagDefinition(FlagNameHelp, "Show help", "", FlagTypeBoolean, "", false, true, false) +} + func newFlagBuilder() *flagBuilder { - return &flagBuilder{map[string]cli.Flag{}, []string{}} + return &flagBuilder{map[string]*flagDefinition{}, []string{}} } diff --git a/commandline/flag_definition.go b/commandline/flag_definition.go new file mode 100644 index 0000000..eb734bc --- /dev/null +++ b/commandline/flag_definition.go @@ -0,0 +1,25 @@ +package commandline + +type flagDefinition struct { + Name string + Summary string + Description string + Type FlagType + EnvVarName string + Value interface{} + Hidden bool + Required bool +} + +func newFlagDefinition(name string, summary string, description string, flagType FlagType, envVarName string, value interface{}, hidden bool, required bool) *flagDefinition { + return &flagDefinition{ + name, + summary, + description, + flagType, + envVarName, + value, + hidden, + required, + } +} diff --git a/commandline/flag_type.go b/commandline/flag_type.go new file mode 100644 index 0000000..68cd6c6 --- /dev/null +++ b/commandline/flag_type.go @@ -0,0 +1,26 @@ +package commandline + +import "fmt" + +type FlagType int + +const ( + FlagTypeString FlagType = iota + 1 + FlagTypeInteger + FlagTypeBoolean + FlagTypeStringArray +) + +func (t FlagType) String() string { + switch t { + case FlagTypeString: + return "string" + case FlagTypeInteger: + return "integer" + case FlagTypeBoolean: + return "boolean" + case FlagTypeStringArray: + return "stringArray" + } + panic(fmt.Sprintf("Unknown flag type: %d", int(t))) +} diff --git a/commandline/show_command_handler.go b/commandline/show_command_handler.go index ec26b86..25f9402 100644 --- a/commandline/show_command_handler.go +++ b/commandline/show_command_handler.go @@ -5,7 +5,6 @@ import ( "sort" "github.com/UiPath/uipathcli/parser" - "github.com/urfave/cli/v2" ) // showCommandHandler prints all available uipathcli commands @@ -29,7 +28,7 @@ type commandJson struct { Subcommands []commandJson `json:"subcommands"` } -func (h showCommandHandler) Execute(definitions []parser.Definition, globalFlags []cli.Flag) (string, error) { +func (h showCommandHandler) Execute(definitions []parser.Definition, globalFlags []*flagDefinition) (string, error) { result := commandJson{ Name: "uipath", Description: "Command line interface to simplify, script and automate API calls for UiPath services", @@ -104,7 +103,7 @@ func (h showCommandHandler) convertOperationToCommand(operation parser.Operation } } -func (h showCommandHandler) convertFlagsToCommandParameters(flags []cli.Flag) []parameterJson { +func (h showCommandHandler) convertFlagsToCommandParameters(flags []*flagDefinition) []parameterJson { result := []parameterJson{} for _, f := range flags { result = append(result, h.convertFlagToCommandParameter(f)) @@ -120,37 +119,14 @@ func (h showCommandHandler) convertParametersToCommandParameters(parameters []pa return result } -func (h showCommandHandler) convertFlagToCommandParameter(flag cli.Flag) parameterJson { - intFlag, ok := flag.(*cli.IntFlag) - if ok { - return parameterJson{ - Name: intFlag.Name, - Description: intFlag.Usage, - Type: "integer", - Required: false, - AllowedValues: []interface{}{}, - DefaultValue: intFlag.Value, - } - } - boolFlag, ok := flag.(*cli.BoolFlag) - if ok { - return parameterJson{ - Name: boolFlag.Name, - Description: boolFlag.Usage, - Type: "boolean", - Required: false, - AllowedValues: []interface{}{}, - DefaultValue: boolFlag.Value, - } - } - stringFlag := flag.(*cli.StringFlag) +func (h showCommandHandler) convertFlagToCommandParameter(flag *flagDefinition) parameterJson { return parameterJson{ - Name: stringFlag.Name, - Description: stringFlag.Usage, - Type: "string", + Name: flag.Name, + Description: flag.Summary, + Type: flag.Type.String(), Required: false, AllowedValues: []interface{}{}, - DefaultValue: stringFlag.Value, + DefaultValue: flag.Value, } }