• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

UiPath / uipathcli / 11162363984

03 Oct 2024 12:53PM UTC coverage: 90.424% (-0.02%) from 90.447%
11162363984

push

github

thschmitt
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.

463 of 478 new or added lines in 8 files covered. (96.86%)

1 existing line in 1 file now uncovered.

4268 of 4720 relevant lines covered (90.42%)

1.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.99
/commandline/command_builder.go
1
package commandline
2

3
import (
4
        "encoding/json"
5
        "errors"
6
        "fmt"
7
        "io"
8
        "net/url"
9
        "sort"
10
        "strings"
11
        "sync"
12
        "time"
13

14
        "github.com/UiPath/uipathcli/config"
15
        "github.com/UiPath/uipathcli/executor"
16
        "github.com/UiPath/uipathcli/log"
17
        "github.com/UiPath/uipathcli/output"
18
        "github.com/UiPath/uipathcli/parser"
19
        "github.com/UiPath/uipathcli/utils"
20
)
21

22
// The CommandBuilder is creating all available operations and arguments for the CLI.
23
type CommandBuilder struct {
24
        Input              utils.Stream
25
        StdIn              io.Reader
26
        StdOut             io.Writer
27
        StdErr             io.Writer
28
        ConfigProvider     config.ConfigProvider
29
        Executor           executor.Executor
30
        PluginExecutor     executor.Executor
31
        DefinitionProvider DefinitionProvider
32
}
33

34
func (b CommandBuilder) sort(commands []*CommandDefinition) {
1✔
35
        sort.Slice(commands, func(i, j int) bool {
2✔
36
                return commands[i].Name < commands[j].Name
1✔
37
        })
1✔
38
}
39

40
func (b CommandBuilder) fileInput(context *CommandExecContext, parameters []parser.Parameter) utils.Stream {
1✔
41
        value := context.String(FlagNameFile)
1✔
42
        if value == "" {
2✔
43
                return nil
1✔
44
        }
1✔
45
        if value == FlagValueFromStdIn {
2✔
46
                return b.Input
1✔
47
        }
1✔
48
        for _, param := range parameters {
2✔
49
                if strings.EqualFold(param.FieldName, FlagNameFile) {
2✔
50
                        return nil
1✔
51
                }
1✔
52
        }
53
        return utils.NewFileStream(value)
1✔
54
}
55

56
func (b CommandBuilder) createExecutionParameters(context *CommandExecContext, config *config.Config, operation parser.Operation) (executor.ExecutionParameters, error) {
1✔
57
        typeConverter := newTypeConverter()
1✔
58

1✔
59
        parameters := []executor.ExecutionParameter{}
1✔
60
        for _, param := range operation.Parameters {
2✔
61
                if context.IsSet(param.Name) && param.IsArray() {
2✔
62
                        value, err := typeConverter.ConvertArray(context.StringSlice(param.Name), param)
1✔
63
                        if err != nil {
2✔
64
                                return nil, err
1✔
65
                        }
1✔
66
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
1✔
67
                        parameters = append(parameters, *parameter)
1✔
68
                } else if context.IsSet(param.Name) {
2✔
69
                        value, err := typeConverter.Convert(context.String(param.Name), param)
1✔
70
                        if err != nil {
2✔
71
                                return nil, err
1✔
72
                        }
1✔
73
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
1✔
74
                        parameters = append(parameters, *parameter)
1✔
75
                } else if configValue, ok := config.Parameter[param.Name]; ok {
2✔
76
                        value, err := typeConverter.Convert(configValue, param)
1✔
77
                        if err != nil {
1✔
78
                                return nil, err
×
79
                        }
×
80
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
1✔
81
                        parameters = append(parameters, *parameter)
1✔
82
                } else if param.Required && param.DefaultValue != nil {
2✔
83
                        parameter := executor.NewExecutionParameter(param.FieldName, param.DefaultValue, param.In)
1✔
84
                        parameters = append(parameters, *parameter)
1✔
85
                }
1✔
86
        }
87
        parameters = append(parameters, b.createExecutionParametersFromConfigMap(config.Header, parser.ParameterInHeader)...)
1✔
88
        return parameters, nil
1✔
89
}
90

91
func (b CommandBuilder) createExecutionParametersFromConfigMap(params map[string]string, in string) executor.ExecutionParameters {
1✔
92
        parameters := []executor.ExecutionParameter{}
1✔
93
        for key, value := range params {
2✔
94
                parameter := executor.NewExecutionParameter(key, value, in)
1✔
95
                parameters = append(parameters, *parameter)
1✔
96
        }
1✔
97
        return parameters
1✔
98
}
99

100
func (b CommandBuilder) formatAllowedValues(values []interface{}) string {
1✔
101
        result := ""
1✔
102
        separator := ""
1✔
103
        for _, value := range values {
2✔
104
                result += fmt.Sprintf("%s%v", separator, value)
1✔
105
                separator = ", "
1✔
106
        }
1✔
107
        return result
1✔
108
}
109

110
func (b CommandBuilder) createFlags(parameters []parser.Parameter) []*FlagDefinition {
1✔
111
        flags := []*FlagDefinition{}
1✔
112
        for _, parameter := range parameters {
2✔
113
                formatter := newParameterFormatter(parameter)
1✔
114
                flagType := FlagTypeString
1✔
115
                if parameter.IsArray() {
2✔
116
                        flagType = FlagTypeStringArray
1✔
117
                }
1✔
118
                flag := NewFlag(parameter.Name, formatter.Description(), flagType)
1✔
119
                flags = append(flags, flag)
1✔
120
        }
121
        return flags
1✔
122
}
123

124
func (b CommandBuilder) sortParameters(parameters []parser.Parameter) {
1✔
125
        sort.Slice(parameters, func(i, j int) bool {
2✔
126
                if parameters[i].Required && !parameters[j].Required {
2✔
127
                        return true
1✔
128
                }
1✔
129
                if !parameters[i].Required && parameters[j].Required {
2✔
130
                        return false
1✔
131
                }
1✔
132
                return parameters[i].Name < parameters[j].Name
1✔
133
        })
134
}
135

136
func (b CommandBuilder) outputFormat(config config.Config, context *CommandExecContext) (string, error) {
1✔
137
        outputFormat := context.String(FlagNameOutputFormat)
1✔
138
        if outputFormat == "" {
2✔
139
                outputFormat = config.Output
1✔
140
        }
1✔
141
        if outputFormat == "" {
2✔
142
                outputFormat = FlagValueOutputFormatJson
1✔
143
        }
1✔
144
        if outputFormat != FlagValueOutputFormatJson && outputFormat != FlagValueOutputFormatText {
1✔
NEW
145
                return "", fmt.Errorf("Invalid output format '%s', allowed values: %s, %s", outputFormat, FlagValueOutputFormatJson, FlagValueOutputFormatText)
×
UNCOV
146
        }
×
147
        return outputFormat, nil
1✔
148
}
149

150
func (b CommandBuilder) createBaseUri(operation parser.Operation, config config.Config, context *CommandExecContext) (url.URL, error) {
1✔
151
        uriArgument, err := b.parseUriArgument(context)
1✔
152
        if err != nil {
1✔
153
                return operation.BaseUri, err
×
154
        }
×
155

156
        builder := NewUriBuilder(operation.BaseUri)
1✔
157
        builder.OverrideUri(config.Uri)
1✔
158
        builder.OverrideUri(uriArgument)
1✔
159
        return builder.Uri(), nil
1✔
160
}
161

162
func (b CommandBuilder) createIdentityUri(context *CommandExecContext, config config.Config, baseUri url.URL) (*url.URL, error) {
1✔
163
        uri := context.String(FlagNameIdentityUri)
1✔
164
        if uri != "" {
2✔
165
                identityUri, err := url.Parse(uri)
1✔
166
                if err != nil {
2✔
167
                        return nil, fmt.Errorf("Error parsing %s argument: %w", FlagNameIdentityUri, err)
1✔
168
                }
1✔
169
                return identityUri, nil
×
170
        }
171

172
        value := config.Auth.Config["uri"]
1✔
173
        uri, valid := value.(string)
1✔
174
        if valid && uri != "" {
2✔
175
                identityUri, err := url.Parse(uri)
1✔
176
                if err != nil {
2✔
177
                        return nil, fmt.Errorf("Error parsing identity uri config: %w", err)
1✔
178
                }
1✔
179
                return identityUri, nil
×
180
        }
181
        identityUri, err := url.Parse(fmt.Sprintf("%s://%s/identity_", baseUri.Scheme, baseUri.Host))
1✔
182
        if err != nil {
1✔
183
                return nil, fmt.Errorf("Error parsing identity uri: %w", err)
×
184
        }
×
185
        return identityUri, nil
1✔
186
}
187

188
func (b CommandBuilder) parseUriArgument(context *CommandExecContext) (*url.URL, error) {
1✔
189
        uriFlag := context.String(FlagNameUri)
1✔
190
        if uriFlag == "" {
2✔
191
                return nil, nil
1✔
192
        }
1✔
193
        uriArgument, err := url.Parse(uriFlag)
1✔
194
        if err != nil {
1✔
NEW
195
                return nil, fmt.Errorf("Error parsing %s argument: %w", FlagNameUri, err)
×
196
        }
×
197
        return uriArgument, nil
1✔
198
}
199

200
func (b CommandBuilder) getValue(parameter parser.Parameter, context *CommandExecContext, config config.Config) string {
1✔
201
        value := context.String(parameter.Name)
1✔
202
        if value != "" {
2✔
203
                return value
1✔
204
        }
1✔
205
        value = config.Parameter[parameter.Name]
1✔
206
        if value != "" {
2✔
207
                return value
1✔
208
        }
1✔
209
        value = config.Header[parameter.Name]
1✔
210
        if value != "" {
1✔
211
                return value
×
212
        }
×
213
        if parameter.DefaultValue != nil {
2✔
214
                return fmt.Sprintf("%v", parameter.DefaultValue)
1✔
215
        }
1✔
216
        return ""
1✔
217
}
218

219
func (b CommandBuilder) validateArguments(context *CommandExecContext, parameters []parser.Parameter, config config.Config) error {
1✔
220
        err := errors.New("Invalid arguments:")
1✔
221
        result := true
1✔
222
        for _, parameter := range parameters {
2✔
223
                value := b.getValue(parameter, context, config)
1✔
224
                if parameter.Required && value == "" {
2✔
225
                        result = false
1✔
226
                        err = fmt.Errorf("%w\n  Argument --%s is missing", err, parameter.Name)
1✔
227
                }
1✔
228
                if value != "" && len(parameter.AllowedValues) > 0 {
2✔
229
                        valid := false
1✔
230
                        for _, allowedValue := range parameter.AllowedValues {
2✔
231
                                if fmt.Sprintf("%v", allowedValue) == value {
2✔
232
                                        valid = true
1✔
233
                                        break
1✔
234
                                }
235
                        }
236
                        if !valid {
2✔
237
                                allowedValues := b.formatAllowedValues(parameter.AllowedValues)
1✔
238
                                result = false
1✔
239
                                err = fmt.Errorf("%w\n  Argument value '%v' for --%s is invalid, allowed values: %s", err, value, parameter.Name, allowedValues)
1✔
240
                        }
1✔
241
                }
242
        }
243
        if result {
2✔
244
                return nil
1✔
245
        }
1✔
246
        return err
1✔
247
}
248

249
func (b CommandBuilder) logger(context executor.ExecutionContext, writer io.Writer) log.Logger {
1✔
250
        if context.Debug {
2✔
251
                return log.NewDebugLogger(writer)
1✔
252
        }
1✔
253
        return log.NewDefaultLogger(writer)
1✔
254
}
255

256
func (b CommandBuilder) outputWriter(writer io.Writer, format string, query string) output.OutputWriter {
1✔
257
        var transformer output.Transformer = output.NewDefaultTransformer()
1✔
258
        if query != "" {
2✔
259
                transformer = output.NewJmesPathTransformer(query)
1✔
260
        }
1✔
261
        if format == FlagValueOutputFormatText {
2✔
262
                return output.NewTextOutputWriter(writer, transformer)
1✔
263
        }
1✔
264
        return output.NewJsonOutputWriter(writer, transformer)
1✔
265
}
266

267
func (b CommandBuilder) executeCommand(context executor.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
268
        if context.Plugin != nil {
2✔
269
                return b.PluginExecutor.Call(context, writer, logger)
1✔
270
        }
1✔
271
        return b.Executor.Call(context, writer, logger)
1✔
272
}
273

274
func (b CommandBuilder) createOperationCommand(operation parser.Operation) *CommandDefinition {
1✔
275
        parameters := operation.Parameters
1✔
276
        b.sortParameters(parameters)
1✔
277

1✔
278
        flags := NewFlagBuilder().
1✔
279
                AddFlags(b.createFlags(parameters)).
1✔
280
                AddDefaultFlags(true).
1✔
281
                AddHelpFlag().
1✔
282
                Build()
1✔
283

1✔
284
        return NewCommand(operation.Name, operation.Summary, operation.Description).
1✔
285
                WithFlags(flags).
1✔
286
                WithHelpTemplate(OperationCommandHelpTemplate).
1✔
287
                WithHidden(operation.Hidden).
1✔
288
                WithAction(func(context *CommandExecContext) error {
2✔
289
                        profileName := context.String(FlagNameProfile)
1✔
290
                        config := b.ConfigProvider.Config(profileName)
1✔
291
                        if config == nil {
2✔
292
                                return fmt.Errorf("Could not find profile '%s'", profileName)
1✔
293
                        }
1✔
294
                        outputFormat, err := b.outputFormat(*config, context)
1✔
295
                        if err != nil {
1✔
296
                                return err
×
297
                        }
×
298
                        query := context.String(FlagNameQuery)
1✔
299
                        wait := context.String(FlagNameWait)
1✔
300
                        waitTimeout := context.Int(FlagNameWaitTimeout)
1✔
301

1✔
302
                        baseUri, err := b.createBaseUri(operation, *config, context)
1✔
303
                        if err != nil {
1✔
304
                                return err
×
305
                        }
×
306

307
                        input := b.fileInput(context, operation.Parameters)
1✔
308
                        if input == nil {
2✔
309
                                err = b.validateArguments(context, operation.Parameters, *config)
1✔
310
                                if err != nil {
2✔
311
                                        return err
1✔
312
                                }
1✔
313
                        }
314

315
                        parameters, err := b.createExecutionParameters(context, config, operation)
1✔
316
                        if err != nil {
2✔
317
                                return err
1✔
318
                        }
1✔
319

320
                        organization := context.String(FlagNameOrganization)
1✔
321
                        if organization == "" {
2✔
322
                                organization = config.Organization
1✔
323
                        }
1✔
324
                        tenant := context.String(FlagNameTenant)
1✔
325
                        if tenant == "" {
2✔
326
                                tenant = config.Tenant
1✔
327
                        }
1✔
328
                        insecure := context.Bool(FlagNameInsecure) || config.Insecure
1✔
329
                        debug := context.Bool(FlagNameDebug) || config.Debug
1✔
330
                        identityUri, err := b.createIdentityUri(context, *config, baseUri)
1✔
331
                        if err != nil {
2✔
332
                                return err
1✔
333
                        }
1✔
334

335
                        executionContext := executor.NewExecutionContext(
1✔
336
                                organization,
1✔
337
                                tenant,
1✔
338
                                operation.Method,
1✔
339
                                baseUri,
1✔
340
                                operation.Route,
1✔
341
                                operation.ContentType,
1✔
342
                                input,
1✔
343
                                parameters,
1✔
344
                                config.Auth,
1✔
345
                                insecure,
1✔
346
                                debug,
1✔
347
                                *identityUri,
1✔
348
                                operation.Plugin)
1✔
349

1✔
350
                        if wait != "" {
2✔
351
                                return b.executeWait(*executionContext, outputFormat, query, wait, waitTimeout)
1✔
352
                        }
1✔
353
                        return b.execute(*executionContext, outputFormat, query, nil)
1✔
354
                })
355
}
356

357
func (b CommandBuilder) executeWait(executionContext executor.ExecutionContext, outputFormat string, query string, wait string, waitTimeout int) error {
1✔
358
        logger := log.NewDefaultLogger(b.StdErr)
1✔
359
        outputWriter := output.NewMemoryOutputWriter()
1✔
360
        for start := time.Now(); time.Since(start) < time.Duration(waitTimeout)*time.Second; {
2✔
361
                err := b.execute(executionContext, "json", "", outputWriter)
1✔
362
                result, evaluationErr := b.evaluateWaitCondition(outputWriter.Response(), wait)
1✔
363
                if evaluationErr != nil {
2✔
364
                        return evaluationErr
1✔
365
                }
1✔
366
                if result {
2✔
367
                        resultWriter := b.outputWriter(b.StdOut, outputFormat, query)
1✔
368
                        _ = resultWriter.WriteResponse(outputWriter.Response())
1✔
369
                        return err
1✔
370
                }
1✔
371
                logger.LogError("Condition is not met yet. Waiting...\n")
1✔
372
                time.Sleep(1 * time.Second)
1✔
373
        }
374
        return errors.New("Timed out waiting for condition")
1✔
375
}
376

377
func (b CommandBuilder) evaluateWaitCondition(response output.ResponseInfo, wait string) (bool, error) {
1✔
378
        body, err := io.ReadAll(response.Body)
1✔
379
        if err != nil {
1✔
380
                return false, nil
×
381
        }
×
382
        var data interface{}
1✔
383
        err = json.Unmarshal(body, &data)
1✔
384
        if err != nil {
1✔
385
                return false, nil
×
386
        }
×
387
        transformer := output.NewJmesPathTransformer(wait)
1✔
388
        result, err := transformer.Execute(data)
1✔
389
        if err != nil {
2✔
390
                return false, err
1✔
391
        }
1✔
392
        value, ok := result.(bool)
1✔
393
        if !ok {
2✔
394
                return false, fmt.Errorf("Error in wait condition: JMESPath expression needs to return boolean")
1✔
395
        }
1✔
396
        return value, nil
1✔
397
}
398

399
func (b CommandBuilder) execute(executionContext executor.ExecutionContext, outputFormat string, query string, outputWriter output.OutputWriter) error {
1✔
400
        var wg sync.WaitGroup
1✔
401
        wg.Add(3)
1✔
402
        reader, writer := io.Pipe()
1✔
403
        go func() {
2✔
404
                defer wg.Done()
1✔
405
                defer reader.Close()
1✔
406
                _, _ = io.Copy(b.StdOut, reader)
1✔
407
        }()
1✔
408
        errorReader, errorWriter := io.Pipe()
1✔
409
        go func() {
2✔
410
                defer wg.Done()
1✔
411
                defer errorReader.Close()
1✔
412
                _, _ = io.Copy(b.StdErr, errorReader)
1✔
413
        }()
1✔
414

415
        var err error
1✔
416
        go func() {
2✔
417
                defer wg.Done()
1✔
418
                defer writer.Close()
1✔
419
                defer errorWriter.Close()
1✔
420
                if outputWriter == nil {
2✔
421
                        outputWriter = b.outputWriter(writer, outputFormat, query)
1✔
422
                }
1✔
423
                logger := b.logger(executionContext, errorWriter)
1✔
424
                err = b.executeCommand(executionContext, outputWriter, logger)
1✔
425
        }()
426

427
        wg.Wait()
1✔
428
        return err
1✔
429
}
430

431
func (b CommandBuilder) createCategoryCommand(operation parser.Operation) *CommandDefinition {
1✔
432
        flags := NewFlagBuilder().
1✔
433
                AddHelpFlag().
1✔
434
                AddVersionFlag(true).
1✔
435
                Build()
1✔
436

1✔
437
        return NewCommand(operation.Category.Name, operation.Category.Summary, operation.Category.Description).
1✔
438
                WithFlags(flags)
1✔
439
}
1✔
440

441
func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, categories map[string]*CommandDefinition) (bool, *CommandDefinition) {
1✔
442
        isNewCategory := false
1✔
443
        operationCommand := b.createOperationCommand(operation)
1✔
444
        command, found := categories[operation.Category.Name]
1✔
445
        if !found {
2✔
446
                command = b.createCategoryCommand(operation)
1✔
447
                categories[operation.Category.Name] = command
1✔
448
                isNewCategory = true
1✔
449
        }
1✔
450
        command.Subcommands = append(command.Subcommands, operationCommand)
1✔
451
        return isNewCategory, command
1✔
452
}
453

454
func (b CommandBuilder) createServiceCommand(definition parser.Definition) *CommandDefinition {
1✔
455
        categories := map[string]*CommandDefinition{}
1✔
456
        commands := []*CommandDefinition{}
1✔
457
        for _, operation := range definition.Operations {
2✔
458
                if operation.Category == nil {
2✔
459
                        command := b.createOperationCommand(operation)
1✔
460
                        commands = append(commands, command)
1✔
461
                        continue
1✔
462
                }
463
                isNewCategory, command := b.createServiceCommandCategory(operation, categories)
1✔
464
                if isNewCategory {
2✔
465
                        commands = append(commands, command)
1✔
466
                }
1✔
467
        }
468
        b.sort(commands)
1✔
469
        for _, command := range commands {
2✔
470
                b.sort(command.Subcommands)
1✔
471
        }
1✔
472

473
        flags := NewFlagBuilder().
1✔
474
                AddHelpFlag().
1✔
475
                AddVersionFlag(true).
1✔
476
                Build()
1✔
477

1✔
478
        return NewCommand(definition.Name, definition.Summary, definition.Description).
1✔
479
                WithFlags(flags).
1✔
480
                WithSubcommands(commands)
1✔
481
}
482

483
func (b CommandBuilder) createAutoCompleteEnableCommand() *CommandDefinition {
1✔
484
        const shellFlagName = "shell"
1✔
485
        const fileFlagName = "file"
1✔
486

1✔
487
        flags := NewFlagBuilder().
1✔
488
                AddFlag(NewFlag(shellFlagName, fmt.Sprintf("%s, %s", AutocompletePowershell, AutocompleteBash), FlagTypeString).
1✔
489
                        WithRequired(true)).
1✔
490
                AddFlag(NewFlag(fileFlagName, "The profile file path", FlagTypeString).
1✔
491
                        WithHidden(true)).
1✔
492
                AddHelpFlag().
1✔
493
                Build()
1✔
494

1✔
495
        return NewCommand("enable", "Enable auto complete", "Enables auto complete in your shell").
1✔
496
                WithFlags(flags).
1✔
497
                WithAction(func(context *CommandExecContext) error {
2✔
498
                        shell := context.String(shellFlagName)
1✔
499
                        filePath := context.String(fileFlagName)
1✔
500
                        handler := newAutoCompleteHandler()
1✔
501
                        output, err := handler.EnableCompleter(shell, filePath)
1✔
502
                        if err != nil {
2✔
503
                                return err
1✔
504
                        }
1✔
505
                        fmt.Fprintln(b.StdOut, output)
1✔
506
                        return nil
1✔
507
                })
508
}
509

510
func (b CommandBuilder) createAutoCompleteCompleteCommand(version string) *CommandDefinition {
1✔
511
        const commandFlagName = "command"
1✔
512

1✔
513
        flags := NewFlagBuilder().
1✔
514
                AddFlag(NewFlag(commandFlagName, "The command to autocomplete", FlagTypeString).
1✔
515
                        WithRequired(true)).
1✔
516
                AddHelpFlag().
1✔
517
                Build()
1✔
518

1✔
519
        return NewCommand("complete", "Autocomplete suggestions", "Returns the autocomplete suggestions").
1✔
520
                WithFlags(flags).
1✔
521
                WithAction(func(context *CommandExecContext) error {
2✔
522
                        commandText := context.String("command")
1✔
523
                        exclude := []string{}
1✔
524
                        for _, flagName := range FlagNamesPredefined {
2✔
525
                                exclude = append(exclude, "--"+flagName)
1✔
526
                        }
1✔
527
                        args := strings.Split(commandText, " ")
1✔
528
                        definitions, err := b.loadAutocompleteDefinitions(args, version)
1✔
529
                        if err != nil {
1✔
530
                                return err
×
531
                        }
×
532
                        commands := b.createServiceCommands(definitions)
1✔
533
                        command := NewCommand("uipath", "", "").
1✔
534
                                WithSubcommands(commands)
1✔
535
                        handler := newAutoCompleteHandler()
1✔
536
                        words := handler.Find(commandText, command, exclude)
1✔
537
                        for _, word := range words {
2✔
538
                                fmt.Fprintln(b.StdOut, word)
1✔
539
                        }
1✔
540
                        return nil
1✔
541
                })
542
}
543

544
func (b CommandBuilder) createAutoCompleteCommand(version string) *CommandDefinition {
1✔
545
        flags := NewFlagBuilder().
1✔
546
                AddHelpFlag().
1✔
547
                Build()
1✔
548

1✔
549
        subcommands := []*CommandDefinition{
1✔
550
                b.createAutoCompleteEnableCommand(),
1✔
551
                b.createAutoCompleteCompleteCommand(version),
1✔
552
        }
1✔
553

1✔
554
        return NewCommand("autocomplete", "Autocompletion", "Commands for autocompletion").
1✔
555
                WithFlags(flags).
1✔
556
                WithSubcommands(subcommands)
1✔
557
}
1✔
558

559
func (b CommandBuilder) createConfigCommand() *CommandDefinition {
1✔
560
        const flagNameAuth = "auth"
1✔
561

1✔
562
        flags := NewFlagBuilder().
1✔
563
                AddFlag(NewFlag(flagNameAuth, fmt.Sprintf("Authorization type: %s, %s, %s", CredentialsAuth, LoginAuth, PatAuth), FlagTypeString)).
1✔
564
                AddFlag(NewFlag(FlagNameProfile, "Profile to configure", FlagTypeString).
1✔
565
                        WithEnvVarName("UIPATH_PROFILE").
1✔
566
                        WithDefaultValue(config.DefaultProfile)).
1✔
567
                AddHelpFlag().
1✔
568
                Build()
1✔
569

1✔
570
        subcommands := []*CommandDefinition{
1✔
571
                b.createConfigSetCommand(),
1✔
572
        }
1✔
573

1✔
574
        return NewCommand("config", "Interactive Configuration", "Interactive command to configure the CLI").
1✔
575
                WithFlags(flags).
1✔
576
                WithSubcommands(subcommands).
1✔
577
                WithAction(func(context *CommandExecContext) error {
2✔
578
                        auth := context.String(flagNameAuth)
1✔
579
                        profileName := context.String(FlagNameProfile)
1✔
580
                        handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider)
1✔
581
                        return handler.Configure(auth, profileName)
1✔
582
                })
1✔
583
}
584

585
func (b CommandBuilder) createConfigSetCommand() *CommandDefinition {
1✔
586
        const flagNameKey = "key"
1✔
587
        const flagNameValue = "value"
1✔
588

1✔
589
        flags := NewFlagBuilder().
1✔
590
                AddFlag(NewFlag(flagNameKey, "The key", FlagTypeString).
1✔
591
                        WithRequired(true)).
1✔
592
                AddFlag(NewFlag(flagNameValue, "The value to set", FlagTypeString).
1✔
593
                        WithRequired(true)).
1✔
594
                AddFlag(NewFlag(FlagNameProfile, "Profile to configure", FlagTypeString).
1✔
595
                        WithEnvVarName("UIPATH_PROFILE").
1✔
596
                        WithDefaultValue(config.DefaultProfile)).
1✔
597
                AddHelpFlag().
1✔
598
                Build()
1✔
599

1✔
600
        return NewCommand("set", "Set config parameters", "Set config parameters").
1✔
601
                WithFlags(flags).
1✔
602
                WithAction(func(context *CommandExecContext) error {
2✔
603
                        profileName := context.String(FlagNameProfile)
1✔
604
                        key := context.String(flagNameKey)
1✔
605
                        value := context.String(flagNameValue)
1✔
606
                        handler := newConfigCommandHandler(b.StdIn, b.StdOut, b.ConfigProvider)
1✔
607
                        return handler.Set(key, value, profileName)
1✔
608
                })
1✔
609
}
610

611
func (b CommandBuilder) loadDefinitions(args []string, version string) ([]parser.Definition, error) {
1✔
612
        if len(args) <= 1 || strings.HasPrefix(args[1], "-") {
2✔
613
                return b.DefinitionProvider.Index(version)
1✔
614
        }
1✔
615
        if len(args) > 1 && args[1] == "commands" {
2✔
616
                return b.loadAllDefinitions(version)
1✔
617
        }
1✔
618
        definition, err := b.DefinitionProvider.Load(args[1], version)
1✔
619
        if definition == nil {
2✔
620
                return nil, err
1✔
621
        }
1✔
622
        return []parser.Definition{*definition}, err
1✔
623
}
624

625
func (b CommandBuilder) loadAllDefinitions(version string) ([]parser.Definition, error) {
1✔
626
        all, err := b.DefinitionProvider.Index(version)
1✔
627
        if err != nil {
1✔
628
                return nil, err
×
629
        }
×
630
        definitions := []parser.Definition{}
1✔
631
        for _, d := range all {
2✔
632
                definition, err := b.DefinitionProvider.Load(d.Name, version)
1✔
633
                if err != nil {
1✔
634
                        return nil, err
×
635
                }
×
636
                if definition != nil {
2✔
637
                        definitions = append(definitions, *definition)
1✔
638
                }
1✔
639
        }
640
        return definitions, nil
1✔
641
}
642

643
func (b CommandBuilder) loadAutocompleteDefinitions(args []string, version string) ([]parser.Definition, error) {
1✔
644
        if len(args) <= 2 {
2✔
645
                return b.DefinitionProvider.Index(version)
1✔
646
        }
1✔
647
        return b.loadDefinitions(args, version)
1✔
648
}
649

650
func (b CommandBuilder) createShowCommand(definitions []parser.Definition) *CommandDefinition {
1✔
651
        flags := NewFlagBuilder().
1✔
652
                AddHelpFlag().
1✔
653
                Build()
1✔
654

1✔
655
        return NewCommand("show", "Print CLI commands", "Print available uipath CLI commands").
1✔
656
                WithFlags(flags).
1✔
657
                WithHidden(true).
1✔
658
                WithAction(func(context *CommandExecContext) error {
2✔
659
                        defaultFlags := NewFlagBuilder().
1✔
660
                                AddDefaultFlags(false).
1✔
661
                                AddHelpFlag().
1✔
662
                                Build()
1✔
663

1✔
664
                        handler := newShowCommandHandler()
1✔
665
                        output, err := handler.Execute(definitions, defaultFlags)
1✔
666
                        if err != nil {
1✔
NEW
667
                                return err
×
NEW
668
                        }
×
669
                        fmt.Fprintln(b.StdOut, output)
1✔
670
                        return nil
1✔
671
                })
672
}
673

674
func (b CommandBuilder) createInspectCommand(definitions []parser.Definition) *CommandDefinition {
1✔
675
        flags := NewFlagBuilder().
1✔
676
                AddHelpFlag().
1✔
677
                Build()
1✔
678

1✔
679
        subcommands := []*CommandDefinition{
1✔
680
                b.createShowCommand(definitions),
1✔
681
        }
1✔
682

1✔
683
        return NewCommand("commands", "Inspect available CLI operations", "Command to inspect available uipath CLI operations").
1✔
684
                WithFlags(flags).
1✔
685
                WithSubcommands(subcommands).
1✔
686
                WithHidden(true)
1✔
687
}
1✔
688

689
func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*CommandDefinition {
1✔
690
        commands := []*CommandDefinition{}
1✔
691
        for _, e := range definitions {
2✔
692
                command := b.createServiceCommand(e)
1✔
693
                commands = append(commands, command)
1✔
694
        }
1✔
695
        return commands
1✔
696
}
697

698
func (b CommandBuilder) parseArgument(args []string, name string) string {
1✔
699
        for i, arg := range args {
2✔
700
                if strings.TrimSpace(arg) == "--"+name {
2✔
701
                        if len(args) > i+1 {
2✔
702
                                return strings.TrimSpace(args[i+1])
1✔
703
                        }
1✔
704
                }
705
        }
706
        return ""
1✔
707
}
708

709
func (b CommandBuilder) versionFromProfile(profile string) string {
1✔
710
        config := b.ConfigProvider.Config(profile)
1✔
711
        if config == nil {
2✔
712
                return ""
1✔
713
        }
1✔
714
        return config.Version
1✔
715
}
716

717
func (b CommandBuilder) Create(args []string) ([]*CommandDefinition, error) {
1✔
718
        version := b.parseArgument(args, FlagNameVersion)
1✔
719
        profile := b.parseArgument(args, FlagNameProfile)
1✔
720
        if version == "" && profile != "" {
2✔
721
                version = b.versionFromProfile(profile)
1✔
722
        }
1✔
723
        definitions, err := b.loadDefinitions(args, version)
1✔
724
        if err != nil {
2✔
725
                return nil, err
1✔
726
        }
1✔
727
        servicesCommands := b.createServiceCommands(definitions)
1✔
728
        autocompleteCommand := b.createAutoCompleteCommand(version)
1✔
729
        configCommand := b.createConfigCommand()
1✔
730
        inspectCommand := b.createInspectCommand(definitions)
1✔
731
        commands := append(servicesCommands, autocompleteCommand, configCommand, inspectCommand)
1✔
732
        return commands, nil
1✔
733
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc