Skip to content

Commit

Permalink
Create command abstraction and split up command builder
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
thschmitt committed Oct 3, 2024
1 parent c936c82 commit 3d23fe8
Show file tree
Hide file tree
Showing 9 changed files with 645 additions and 489 deletions.
43 changes: 15 additions & 28 deletions commandline/autocomplete_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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))
Expand Down
135 changes: 132 additions & 3 deletions commandline/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ func (c Cli) run(args []string, input utils.Stream) error {
PluginExecutor: c.pluginExecutor,
DefinitionProvider: c.definitionProvider,
}
flags := CommandBuilder.CreateDefaultFlags(false)

flags := NewFlagBuilder().
AddDefaultFlags(false).
Build()

commands, err := CommandBuilder.Create(args)
if err != nil {
return err
Expand All @@ -51,8 +55,8 @@ func (c Cli) run(args []string, input utils.Stream) error {
Usage: "Command-Line Interface for UiPath Services",
UsageText: "uipath <service> <operation> --parameter",
Version: "1.0",
Flags: flags,
Commands: commands,
Flags: c.convertFlags(flags...),
Commands: c.convertCommands(commands...),
Writer: c.stdOut,
ErrWriter: c.stdErr,
HideVersion: true,
Expand Down Expand Up @@ -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.HelpTemplate,
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.DefaultValue != nil {
value = cli.NewStringSlice(flag.DefaultValue.([]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.DefaultValue != nil {
value = flag.DefaultValue.(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.DefaultValue != nil {
value = flag.DefaultValue.(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.DefaultValue != nil {
value = flag.DefaultValue.(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
}
Loading

0 comments on commit 3d23fe8

Please sign in to comment.