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

UiPath / uipathcli / 6442892572

07 Oct 2023 06:45PM UTC coverage: 67.119% (-23.3%) from 90.418%
6442892572

push

github

thschmitt
Upgrade to golang 1.21, update dependencies to latest versions

- Upgrade to golang 1.21
- Update all dependencies to latest versions

3068 of 4571 relevant lines covered (67.12%)

362.28 hits per line

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

75.32
/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
        "github.com/urfave/cli/v2"
21
)
22

23
const FromStdIn = "-"
24

25
const insecureFlagName = "insecure"
26
const debugFlagName = "debug"
27
const profileFlagName = "profile"
28
const uriFlagName = "uri"
29
const organizationFlagName = "organization"
30
const tenantFlagName = "tenant"
31
const helpFlagName = "help"
32
const outputFormatFlagName = "output"
33
const queryFlagName = "query"
34
const waitFlagName = "wait"
35
const waitTimeoutFlagName = "wait-timeout"
36
const versionFlagName = "version"
37
const fileFlagName = "file"
38

39
var predefinedFlags = []string{
40
        insecureFlagName,
41
        debugFlagName,
42
        profileFlagName,
43
        uriFlagName,
44
        organizationFlagName,
45
        tenantFlagName,
46
        helpFlagName,
47
        outputFormatFlagName,
48
        queryFlagName,
49
        waitFlagName,
50
        waitTimeoutFlagName,
51
        versionFlagName,
52
        fileFlagName,
53
}
54

55
const outputFormatJson = "json"
56
const outputFormatText = "text"
57

58
const subcommandHelpTemplate = `NAME:
59
   {{template "helpNameTemplate" .}}
60

61
USAGE:
62
   {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}}{{if .ArgsUsage}}{{.ArgsUsage}}{{else}} [arguments...]{{end}}{{end}}{{if .Description}}
63

64
DESCRIPTION:
65
   {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
66

67
COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
68

69
OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
70

71
OPTIONS:{{range $i, $e := .VisibleFlags}}
72
   --{{$e.Name}} {{wrap $e.Usage 6}}
73
{{end}}{{end}}
74
`
75

76
// The CommandBuilder is creating all available operations and arguments for the CLI.
77
type CommandBuilder struct {
78
        Input              utils.Stream
79
        StdIn              io.Reader
80
        StdOut             io.Writer
81
        StdErr             io.Writer
82
        ConfigProvider     config.ConfigProvider
83
        Executor           executor.Executor
84
        PluginExecutor     executor.Executor
85
        DefinitionProvider DefinitionProvider
86
}
87

88
func (b CommandBuilder) sort(commands []*cli.Command) {
216✔
89
        sort.Slice(commands, func(i, j int) bool {
3,139✔
90
                return commands[i].Name < commands[j].Name
2,923✔
91
        })
2,923✔
92
}
93

94
func (b CommandBuilder) fileInput(context *cli.Context, parameters []parser.Parameter) utils.Stream {
33✔
95
        value := context.String(fileFlagName)
33✔
96
        if value == "" {
45✔
97
                return nil
12✔
98
        }
12✔
99
        if value == FromStdIn {
22✔
100
                return b.Input
1✔
101
        }
1✔
102
        for _, param := range parameters {
40✔
103
                if strings.EqualFold(param.FieldName, fileFlagName) {
40✔
104
                        return nil
20✔
105
                }
20✔
106
        }
107
        return utils.NewFileStream(value)
×
108
}
109

110
func (b CommandBuilder) createExecutionParameters(context *cli.Context, config *config.Config, operation parser.Operation) (executor.ExecutionParameters, error) {
24✔
111
        typeConverter := newTypeConverter()
24✔
112

24✔
113
        parameters := []executor.ExecutionParameter{}
24✔
114
        for _, param := range operation.Parameters {
99✔
115
                if context.IsSet(param.Name) && param.IsArray() {
75✔
116
                        value, err := typeConverter.ConvertArray(context.StringSlice(param.Name), param)
×
117
                        if err != nil {
×
118
                                return nil, err
×
119
                        }
×
120
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
×
121
                        parameters = append(parameters, *parameter)
×
122
                } else if context.IsSet(param.Name) {
142✔
123
                        value, err := typeConverter.Convert(context.String(param.Name), param)
67✔
124
                        if err != nil {
68✔
125
                                return nil, err
1✔
126
                        }
1✔
127
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
66✔
128
                        parameters = append(parameters, *parameter)
66✔
129
                } else if configValue, ok := config.Parameter[param.Name]; ok {
8✔
130
                        value, err := typeConverter.Convert(configValue, param)
×
131
                        if err != nil {
×
132
                                return nil, err
×
133
                        }
×
134
                        parameter := executor.NewExecutionParameter(param.FieldName, value, param.In)
×
135
                        parameters = append(parameters, *parameter)
×
136
                } else if param.Required && param.DefaultValue != nil {
8✔
137
                        parameter := executor.NewExecutionParameter(param.FieldName, param.DefaultValue, param.In)
×
138
                        parameters = append(parameters, *parameter)
×
139
                }
×
140
        }
141
        parameters = append(parameters, b.createExecutionParametersFromConfigMap(config.Header, parser.ParameterInHeader)...)
23✔
142
        return parameters, nil
23✔
143
}
144

145
func (b CommandBuilder) createExecutionParametersFromConfigMap(params map[string]string, in string) executor.ExecutionParameters {
23✔
146
        parameters := []executor.ExecutionParameter{}
23✔
147
        for key, value := range params {
23✔
148
                parameter := executor.NewExecutionParameter(key, value, in)
×
149
                parameters = append(parameters, *parameter)
×
150
        }
×
151
        return parameters
23✔
152
}
153

154
func (b CommandBuilder) formatAllowedValues(values []interface{}) string {
×
155
        result := ""
×
156
        separator := ""
×
157
        for _, value := range values {
×
158
                result += fmt.Sprintf("%s%v", separator, value)
×
159
                separator = ", "
×
160
        }
×
161
        return result
×
162
}
163

164
func (b CommandBuilder) createFlags(parameters []parser.Parameter) []cli.Flag {
906✔
165
        flags := []cli.Flag{}
906✔
166
        for _, parameter := range parameters {
6,416✔
167
                formatter := newParameterFormatter(parameter)
5,510✔
168
                if parameter.IsArray() {
5,787✔
169
                        flag := cli.StringSliceFlag{
277✔
170
                                Name:  parameter.Name,
277✔
171
                                Usage: formatter.Description(),
277✔
172
                        }
277✔
173
                        flags = append(flags, &flag)
277✔
174
                } else {
5,510✔
175
                        flag := cli.StringFlag{
5,233✔
176
                                Name:  parameter.Name,
5,233✔
177
                                Usage: formatter.Description(),
5,233✔
178
                        }
5,233✔
179
                        flags = append(flags, &flag)
5,233✔
180
                }
5,233✔
181
        }
182
        return flags
906✔
183
}
184

185
func (b CommandBuilder) sortParameters(parameters []parser.Parameter) {
906✔
186
        sort.Slice(parameters, func(i, j int) bool {
15,364✔
187
                if parameters[i].Required && !parameters[j].Required {
17,028✔
188
                        return true
2,570✔
189
                }
2,570✔
190
                if !parameters[i].Required && parameters[j].Required {
12,480✔
191
                        return false
592✔
192
                }
592✔
193
                return parameters[i].Name < parameters[j].Name
11,296✔
194
        })
195
}
196

197
func (b CommandBuilder) outputFormat(config config.Config, context *cli.Context) (string, error) {
33✔
198
        outputFormat := context.String(outputFormatFlagName)
33✔
199
        if outputFormat == "" {
66✔
200
                outputFormat = config.Output
33✔
201
        }
33✔
202
        if outputFormat == "" {
66✔
203
                outputFormat = outputFormatJson
33✔
204
        }
33✔
205
        if outputFormat != outputFormatJson && outputFormat != outputFormatText {
33✔
206
                return "", fmt.Errorf("Invalid output format '%s', allowed values: %s, %s", outputFormat, outputFormatJson, outputFormatText)
×
207
        }
×
208
        return outputFormat, nil
33✔
209
}
210

211
func (b CommandBuilder) createBaseUri(operation parser.Operation, config config.Config, context *cli.Context) (url.URL, error) {
33✔
212
        uriArgument, err := b.parseUriArgument(context)
33✔
213
        if err != nil {
33✔
214
                return operation.BaseUri, err
×
215
        }
×
216

217
        builder := NewUriBuilder(operation.BaseUri)
33✔
218
        builder.OverrideUri(config.Uri)
33✔
219
        builder.OverrideUri(uriArgument)
33✔
220
        return builder.Uri(), nil
33✔
221
}
222

223
func (b CommandBuilder) parseUriArgument(context *cli.Context) (*url.URL, error) {
33✔
224
        uriFlag := context.String(uriFlagName)
33✔
225
        if uriFlag == "" {
51✔
226
                return nil, nil
18✔
227
        }
18✔
228
        uriArgument, err := url.Parse(uriFlag)
15✔
229
        if err != nil {
15✔
230
                return nil, fmt.Errorf("Error parsing %s argument: %w", uriFlagName, err)
×
231
        }
×
232
        return uriArgument, nil
15✔
233
}
234

235
func (b CommandBuilder) getValue(parameter parser.Parameter, context *cli.Context, config config.Config) string {
105✔
236
        value := context.String(parameter.Name)
105✔
237
        if value != "" {
191✔
238
                return value
86✔
239
        }
86✔
240
        value = config.Parameter[parameter.Name]
19✔
241
        if value != "" {
19✔
242
                return value
×
243
        }
×
244
        value = config.Header[parameter.Name]
19✔
245
        if value != "" {
19✔
246
                return value
×
247
        }
×
248
        if parameter.DefaultValue != nil {
19✔
249
                return fmt.Sprintf("%v", parameter.DefaultValue)
×
250
        }
×
251
        return ""
19✔
252
}
253

254
func (b CommandBuilder) validateArguments(context *cli.Context, parameters []parser.Parameter, config config.Config) error {
32✔
255
        err := errors.New("Invalid arguments:")
32✔
256
        result := true
32✔
257
        for _, parameter := range parameters {
137✔
258
                value := b.getValue(parameter, context, config)
105✔
259
                if parameter.Required && value == "" {
114✔
260
                        result = false
9✔
261
                        err = fmt.Errorf("%w\n  Argument --%s is missing", err, parameter.Name)
9✔
262
                }
9✔
263
                if value != "" && len(parameter.AllowedValues) > 0 {
105✔
264
                        valid := false
×
265
                        for _, allowedValue := range parameter.AllowedValues {
×
266
                                if fmt.Sprintf("%v", allowedValue) == value {
×
267
                                        valid = true
×
268
                                        break
×
269
                                }
270
                        }
271
                        if !valid {
×
272
                                allowedValues := b.formatAllowedValues(parameter.AllowedValues)
×
273
                                result = false
×
274
                                err = fmt.Errorf("%w\n  Argument value '%v' for --%s is invalid, allowed values: %s", err, value, parameter.Name, allowedValues)
×
275
                        }
×
276
                }
277
        }
278
        if result {
55✔
279
                return nil
23✔
280
        }
23✔
281
        return err
9✔
282
}
283

284
func (b CommandBuilder) logger(context executor.ExecutionContext, writer io.Writer) log.Logger {
23✔
285
        if context.Debug {
26✔
286
                return log.NewDebugLogger(writer)
3✔
287
        }
3✔
288
        return log.NewDefaultLogger(writer)
20✔
289
}
290

291
func (b CommandBuilder) outputWriter(writer io.Writer, format string, query string) output.OutputWriter {
23✔
292
        var transformer output.Transformer = output.NewDefaultTransformer()
23✔
293
        if query != "" {
23✔
294
                transformer = output.NewJmesPathTransformer(query)
×
295
        }
×
296
        if format == outputFormatText {
23✔
297
                return output.NewTextOutputWriter(writer, transformer)
×
298
        }
×
299
        return output.NewJsonOutputWriter(writer, transformer)
23✔
300
}
301

302
func (b CommandBuilder) executeCommand(context executor.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
23✔
303
        if context.Plugin != nil {
45✔
304
                return b.PluginExecutor.Call(context, writer, logger)
22✔
305
        }
22✔
306
        return b.Executor.Call(context, writer, logger)
1✔
307
}
308

309
func (b CommandBuilder) createOperationCommand(operation parser.Operation) *cli.Command {
906✔
310
        parameters := operation.Parameters
906✔
311
        b.sortParameters(parameters)
906✔
312

906✔
313
        flagBuilder := newFlagBuilder()
906✔
314
        flagBuilder.AddFlags(b.createFlags(parameters))
906✔
315
        flagBuilder.AddFlags(b.CreateDefaultFlags(true))
906✔
316
        flagBuilder.AddFlag(b.HelpFlag())
906✔
317

906✔
318
        return &cli.Command{
906✔
319
                Name:               operation.Name,
906✔
320
                Usage:              operation.Summary,
906✔
321
                Description:        operation.Description,
906✔
322
                Flags:              flagBuilder.ToList(),
906✔
323
                CustomHelpTemplate: subcommandHelpTemplate,
906✔
324
                Action: func(context *cli.Context) error {
939✔
325
                        profileName := context.String(profileFlagName)
33✔
326
                        config := b.ConfigProvider.Config(profileName)
33✔
327
                        if config == nil {
33✔
328
                                return fmt.Errorf("Could not find profile '%s'", profileName)
×
329
                        }
×
330
                        outputFormat, err := b.outputFormat(*config, context)
33✔
331
                        if err != nil {
33✔
332
                                return err
×
333
                        }
×
334
                        query := context.String(queryFlagName)
33✔
335
                        wait := context.String(waitFlagName)
33✔
336
                        waitTimeout := context.Int(waitTimeoutFlagName)
33✔
337

33✔
338
                        baseUri, err := b.createBaseUri(operation, *config, context)
33✔
339
                        if err != nil {
33✔
340
                                return err
×
341
                        }
×
342

343
                        input := b.fileInput(context, operation.Parameters)
33✔
344
                        if input == nil {
65✔
345
                                err = b.validateArguments(context, operation.Parameters, *config)
32✔
346
                                if err != nil {
41✔
347
                                        return err
9✔
348
                                }
9✔
349
                        }
350

351
                        parameters, err := b.createExecutionParameters(context, config, operation)
24✔
352
                        if err != nil {
25✔
353
                                return err
1✔
354
                        }
1✔
355

356
                        organization := context.String(organizationFlagName)
23✔
357
                        if organization == "" {
39✔
358
                                organization = config.Organization
16✔
359
                        }
16✔
360
                        tenant := context.String(tenantFlagName)
23✔
361
                        if tenant == "" {
42✔
362
                                tenant = config.Tenant
19✔
363
                        }
19✔
364
                        insecure := context.Bool(insecureFlagName) || config.Insecure
23✔
365
                        debug := context.Bool(debugFlagName) || config.Debug
23✔
366
                        executionContext := executor.NewExecutionContext(
23✔
367
                                organization,
23✔
368
                                tenant,
23✔
369
                                operation.Method,
23✔
370
                                baseUri,
23✔
371
                                operation.Route,
23✔
372
                                operation.ContentType,
23✔
373
                                input,
23✔
374
                                parameters,
23✔
375
                                config.Auth,
23✔
376
                                insecure,
23✔
377
                                debug,
23✔
378
                                operation.Plugin)
23✔
379

23✔
380
                        if wait != "" {
23✔
381
                                return b.executeWait(*executionContext, outputFormat, query, wait, waitTimeout)
×
382
                        }
×
383
                        return b.execute(*executionContext, outputFormat, query, nil)
23✔
384
                },
385
                HideHelp: true,
386
                Hidden:   operation.Hidden,
387
        }
388
}
389

390
func (b CommandBuilder) executeWait(executionContext executor.ExecutionContext, outputFormat string, query string, wait string, waitTimeout int) error {
×
391
        logger := log.NewDefaultLogger(b.StdErr)
×
392
        outputWriter := output.NewMemoryOutputWriter()
×
393
        for start := time.Now(); time.Since(start) < time.Duration(waitTimeout)*time.Second; {
×
394
                err := b.execute(executionContext, "json", "", outputWriter)
×
395
                result, evaluationErr := b.evaluateWaitCondition(outputWriter.Response(), wait)
×
396
                if evaluationErr != nil {
×
397
                        return evaluationErr
×
398
                }
×
399
                if result {
×
400
                        resultWriter := b.outputWriter(b.StdOut, outputFormat, query)
×
401
                        _ = resultWriter.WriteResponse(outputWriter.Response())
×
402
                        return err
×
403
                }
×
404
                logger.LogError("Condition is not met yet. Waiting...\n")
×
405
                time.Sleep(1 * time.Second)
×
406
        }
407
        return errors.New("Timed out waiting for condition")
×
408
}
409

410
func (b CommandBuilder) evaluateWaitCondition(response output.ResponseInfo, wait string) (bool, error) {
×
411
        body, err := io.ReadAll(response.Body)
×
412
        if err != nil {
×
413
                return false, nil
×
414
        }
×
415
        var data interface{}
×
416
        err = json.Unmarshal(body, &data)
×
417
        if err != nil {
×
418
                return false, nil
×
419
        }
×
420
        transformer := output.NewJmesPathTransformer(wait)
×
421
        result, err := transformer.Execute(data)
×
422
        if err != nil {
×
423
                return false, err
×
424
        }
×
425
        value, ok := result.(bool)
×
426
        if !ok {
×
427
                return false, fmt.Errorf("Error in wait condition: JMESPath expression needs to return boolean")
×
428
        }
×
429
        return value, nil
×
430
}
431

432
func (b CommandBuilder) execute(executionContext executor.ExecutionContext, outputFormat string, query string, outputWriter output.OutputWriter) error {
23✔
433
        var wg sync.WaitGroup
23✔
434
        wg.Add(3)
23✔
435
        reader, writer := io.Pipe()
23✔
436
        go func() {
46✔
437
                defer wg.Done()
23✔
438
                defer reader.Close()
23✔
439
                _, _ = io.Copy(b.StdOut, reader)
23✔
440
        }()
23✔
441
        errorReader, errorWriter := io.Pipe()
23✔
442
        go func() {
46✔
443
                defer wg.Done()
23✔
444
                defer errorReader.Close()
23✔
445
                _, _ = io.Copy(b.StdErr, errorReader)
23✔
446
        }()
23✔
447

448
        var err error
23✔
449
        go func() {
46✔
450
                defer wg.Done()
23✔
451
                defer writer.Close()
23✔
452
                defer errorWriter.Close()
23✔
453
                if outputWriter == nil {
46✔
454
                        outputWriter = b.outputWriter(writer, outputFormat, query)
23✔
455
                }
23✔
456
                logger := b.logger(executionContext, errorWriter)
23✔
457
                err = b.executeCommand(executionContext, outputWriter, logger)
23✔
458
        }()
459

460
        wg.Wait()
23✔
461
        return err
23✔
462
}
463

464
func (b CommandBuilder) createCategoryCommand(operation parser.Operation) *cli.Command {
171✔
465
        return &cli.Command{
171✔
466
                Name:        operation.Category.Name,
171✔
467
                Description: operation.Category.Description,
171✔
468
                Flags: []cli.Flag{
171✔
469
                        b.HelpFlag(),
171✔
470
                        b.VersionFlag(true),
171✔
471
                },
171✔
472
                HideHelp: true,
171✔
473
        }
171✔
474
}
171✔
475

476
func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, categories map[string]*cli.Command) (bool, *cli.Command) {
903✔
477
        isNewCategory := false
903✔
478
        operationCommand := b.createOperationCommand(operation)
903✔
479
        command, found := categories[operation.Category.Name]
903✔
480
        if !found {
1,074✔
481
                command = b.createCategoryCommand(operation)
171✔
482
                categories[operation.Category.Name] = command
171✔
483
                isNewCategory = true
171✔
484
        }
171✔
485
        command.Subcommands = append(command.Subcommands, operationCommand)
903✔
486
        return isNewCategory, command
903✔
487
}
488

489
func (b CommandBuilder) createServiceCommand(definition parser.Definition) *cli.Command {
42✔
490
        categories := map[string]*cli.Command{}
42✔
491
        commands := []*cli.Command{}
42✔
492
        for _, operation := range definition.Operations {
948✔
493
                if operation.Category == nil {
909✔
494
                        command := b.createOperationCommand(operation)
3✔
495
                        commands = append(commands, command)
3✔
496
                        continue
3✔
497
                }
498
                isNewCategory, command := b.createServiceCommandCategory(operation, categories)
903✔
499
                if isNewCategory {
1,074✔
500
                        commands = append(commands, command)
171✔
501
                }
171✔
502
        }
503
        b.sort(commands)
42✔
504
        for _, command := range commands {
216✔
505
                b.sort(command.Subcommands)
174✔
506
        }
174✔
507

508
        return &cli.Command{
42✔
509
                Name:        definition.Name,
42✔
510
                Description: definition.Description,
42✔
511
                Flags: []cli.Flag{
42✔
512
                        b.HelpFlag(),
42✔
513
                        b.VersionFlag(true),
42✔
514
                },
42✔
515
                Subcommands: commands,
42✔
516
                HideHelp:    true,
42✔
517
        }
42✔
518
}
519

520
func (b CommandBuilder) createAutoCompleteEnableCommand() *cli.Command {
39✔
521
        const shellFlagName = "shell"
39✔
522
        const powershellFlagValue = "powershell"
39✔
523
        const bashFlagValue = "bash"
39✔
524
        const fileFlagName = "file"
39✔
525

39✔
526
        return &cli.Command{
39✔
527
                Name:        "enable",
39✔
528
                Description: "Enables auto complete in your shell",
39✔
529
                Flags: []cli.Flag{
39✔
530
                        &cli.StringFlag{
39✔
531
                                Name:     shellFlagName,
39✔
532
                                Usage:    fmt.Sprintf("%s, %s", powershellFlagValue, bashFlagValue),
39✔
533
                                Required: true,
39✔
534
                        },
39✔
535
                        &cli.StringFlag{
39✔
536
                                Name:   fileFlagName,
39✔
537
                                Hidden: true,
39✔
538
                        },
39✔
539
                        b.HelpFlag(),
39✔
540
                },
39✔
541
                Action: func(context *cli.Context) error {
39✔
542
                        shell := context.String(shellFlagName)
×
543
                        filePath := context.String(fileFlagName)
×
544
                        handler := newAutoCompleteHandler()
×
545
                        output, err := handler.EnableCompleter(shell, filePath)
×
546
                        if err != nil {
×
547
                                return err
×
548
                        }
×
549
                        fmt.Fprintln(b.StdOut, output)
×
550
                        return nil
×
551
                },
552
                HideHelp: true,
553
        }
554
}
555

556
func (b CommandBuilder) createAutoCompleteCompleteCommand(version string) *cli.Command {
39✔
557
        return &cli.Command{
39✔
558
                Name:        "complete",
39✔
559
                Description: "Returns the autocomplete suggestions",
39✔
560
                Flags: []cli.Flag{
39✔
561
                        &cli.StringFlag{
39✔
562
                                Name:     "command",
39✔
563
                                Usage:    "The command to autocomplete",
39✔
564
                                Required: true,
39✔
565
                        },
39✔
566
                        b.HelpFlag(),
39✔
567
                },
39✔
568
                Action: func(context *cli.Context) error {
40✔
569
                        commandText := context.String("command")
1✔
570
                        exclude := []string{}
1✔
571
                        for _, flagName := range predefinedFlags {
14✔
572
                                exclude = append(exclude, "--"+flagName)
13✔
573
                        }
13✔
574
                        args := strings.Split(commandText, " ")
1✔
575
                        definitions, err := b.loadAutocompleteDefinitions(args, version)
1✔
576
                        if err != nil {
1✔
577
                                return err
×
578
                        }
×
579
                        commands := b.createServiceCommands(definitions)
1✔
580
                        handler := newAutoCompleteHandler()
1✔
581
                        words := handler.Find(commandText, commands, exclude)
1✔
582
                        for _, word := range words {
2✔
583
                                fmt.Fprintln(b.StdOut, word)
1✔
584
                        }
1✔
585
                        return nil
1✔
586
                },
587
                HideHelp: true,
588
        }
589
}
590

591
func (b CommandBuilder) createAutoCompleteCommand(version string) *cli.Command {
39✔
592
        return &cli.Command{
39✔
593
                Name:        "autocomplete",
39✔
594
                Description: "Commands for autocompletion",
39✔
595
                Flags: []cli.Flag{
39✔
596
                        b.HelpFlag(),
39✔
597
                },
39✔
598
                Subcommands: []*cli.Command{
39✔
599
                        b.createAutoCompleteEnableCommand(),
39✔
600
                        b.createAutoCompleteCompleteCommand(version),
39✔
601
                },
39✔
602
                HideHelp: true,
39✔
603
        }
39✔
604
}
39✔
605

606
func (b CommandBuilder) createConfigCommand() *cli.Command {
39✔
607
        authFlagName := "auth"
39✔
608
        flags := []cli.Flag{
39✔
609
                &cli.StringFlag{
39✔
610
                        Name:  authFlagName,
39✔
611
                        Usage: fmt.Sprintf("Authorization type: %s, %s, %s", CredentialsAuth, LoginAuth, PatAuth),
39✔
612
                },
39✔
613
                &cli.StringFlag{
39✔
614
                        Name:    profileFlagName,
39✔
615
                        Usage:   "Profile to configure",
39✔
616
                        EnvVars: []string{"UIPATH_PROFILE"},
39✔
617
                        Value:   config.DefaultProfile,
39✔
618
                },
39✔
619
                b.HelpFlag(),
39✔
620
        }
39✔
621

39✔
622
        return &cli.Command{
39✔
623
                Name:        "config",
39✔
624
                Description: "Interactive command to configure the CLI",
39✔
625
                Flags:       flags,
39✔
626
                Subcommands: []*cli.Command{
39✔
627
                        b.createConfigSetCommand(),
39✔
628
                },
39✔
629
                Action: func(context *cli.Context) error {
39✔
630
                        auth := context.String(authFlagName)
×
631
                        profileName := context.String(profileFlagName)
×
632
                        handler := ConfigCommandHandler{
×
633
                                StdIn:          b.StdIn,
×
634
                                StdOut:         b.StdOut,
×
635
                                ConfigProvider: b.ConfigProvider,
×
636
                        }
×
637
                        return handler.Configure(auth, profileName)
×
638
                },
×
639
                HideHelp: true,
640
        }
641
}
642

643
func (b CommandBuilder) createConfigSetCommand() *cli.Command {
39✔
644
        keyFlagName := "key"
39✔
645
        valueFlagName := "value"
39✔
646
        flags := []cli.Flag{
39✔
647
                &cli.StringFlag{
39✔
648
                        Name:     keyFlagName,
39✔
649
                        Usage:    "The key",
39✔
650
                        Required: true,
39✔
651
                },
39✔
652
                &cli.StringFlag{
39✔
653
                        Name:     valueFlagName,
39✔
654
                        Usage:    "The value to set",
39✔
655
                        Required: true,
39✔
656
                },
39✔
657
                &cli.StringFlag{
39✔
658
                        Name:    profileFlagName,
39✔
659
                        Usage:   "Profile to configure",
39✔
660
                        EnvVars: []string{"UIPATH_PROFILE"},
39✔
661
                        Value:   config.DefaultProfile,
39✔
662
                },
39✔
663
                b.HelpFlag(),
39✔
664
        }
39✔
665
        return &cli.Command{
39✔
666
                Name:        "set",
39✔
667
                Description: "Set config parameters",
39✔
668
                Flags:       flags,
39✔
669
                Action: func(context *cli.Context) error {
39✔
670
                        profileName := context.String(profileFlagName)
×
671
                        key := context.String(keyFlagName)
×
672
                        value := context.String(valueFlagName)
×
673
                        handler := ConfigCommandHandler{
×
674
                                StdIn:          b.StdIn,
×
675
                                StdOut:         b.StdOut,
×
676
                                ConfigProvider: b.ConfigProvider,
×
677
                        }
×
678
                        return handler.Set(key, value, profileName)
×
679
                },
×
680
                HideHelp: true,
681
        }
682
}
683

684
func (b CommandBuilder) loadDefinitions(args []string, version string) ([]parser.Definition, error) {
40✔
685
        if len(args) <= 1 || strings.HasPrefix(args[1], "--") {
41✔
686
                return b.DefinitionProvider.Index(version)
1✔
687
        }
1✔
688
        if len(args) > 1 && args[1] == "commands" {
39✔
689
                return b.loadAllDefinitions(version)
×
690
        }
×
691
        definition, err := b.DefinitionProvider.Load(args[1], version)
39✔
692
        if definition == nil {
40✔
693
                return nil, err
1✔
694
        }
1✔
695
        return []parser.Definition{*definition}, err
38✔
696
}
697

698
func (b CommandBuilder) loadAllDefinitions(version string) ([]parser.Definition, error) {
×
699
        all, err := b.DefinitionProvider.Index(version)
×
700
        if err != nil {
×
701
                return nil, err
×
702
        }
×
703
        definitions := []parser.Definition{}
×
704
        for _, d := range all {
×
705
                definition, err := b.DefinitionProvider.Load(d.Name, version)
×
706
                if err != nil {
×
707
                        return nil, err
×
708
                }
×
709
                if definition != nil {
×
710
                        definitions = append(definitions, *definition)
×
711
                }
×
712
        }
713
        return definitions, nil
×
714
}
715

716
func (b CommandBuilder) loadAutocompleteDefinitions(args []string, version string) ([]parser.Definition, error) {
1✔
717
        if len(args) <= 2 {
1✔
718
                return b.DefinitionProvider.Index(version)
×
719
        }
×
720
        return b.loadDefinitions(args, version)
1✔
721
}
722

723
func (b CommandBuilder) createShowCommand(definitions []parser.Definition, commands []*cli.Command) *cli.Command {
39✔
724
        return &cli.Command{
39✔
725
                Name:        "commands",
39✔
726
                Description: "Command to inspect available uipath CLI operations",
39✔
727
                Flags: []cli.Flag{
39✔
728
                        b.HelpFlag(),
39✔
729
                },
39✔
730
                Subcommands: []*cli.Command{
39✔
731
                        {
39✔
732
                                Name:        "show",
39✔
733
                                Description: "Print available uipath CLI commands",
39✔
734
                                Flags: []cli.Flag{
39✔
735
                                        b.HelpFlag(),
39✔
736
                                },
39✔
737
                                Action: func(context *cli.Context) error {
39✔
738
                                        flagBuilder := newFlagBuilder()
×
739
                                        flagBuilder.AddFlags(b.CreateDefaultFlags(false))
×
740
                                        flagBuilder.AddFlag(b.HelpFlag())
×
741
                                        flags := flagBuilder.ToList()
×
742

×
743
                                        handler := newShowCommandHandler()
×
744
                                        output, err := handler.Execute(definitions, flags)
×
745
                                        if err != nil {
×
746
                                                return err
×
747
                                        }
×
748
                                        fmt.Fprintln(b.StdOut, output)
×
749
                                        return nil
×
750
                                },
751
                                HideHelp: true,
752
                                Hidden:   true,
753
                        },
754
                },
755
                HideHelp: true,
756
                Hidden:   true,
757
        }
758
}
759

760
func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*cli.Command {
40✔
761
        commands := []*cli.Command{}
40✔
762
        for _, e := range definitions {
82✔
763
                command := b.createServiceCommand(e)
42✔
764
                commands = append(commands, command)
42✔
765
        }
42✔
766
        return commands
40✔
767
}
768

769
func (b CommandBuilder) parseArgument(args []string, name string) string {
78✔
770
        for i, arg := range args {
842✔
771
                if strings.TrimSpace(arg) == "--"+name {
764✔
772
                        if len(args) > i+1 {
×
773
                                return strings.TrimSpace(args[i+1])
×
774
                        }
×
775
                }
776
        }
777
        return ""
78✔
778
}
779

780
func (b CommandBuilder) versionFromProfile(profile string) string {
×
781
        config := b.ConfigProvider.Config(profile)
×
782
        if config == nil {
×
783
                return ""
×
784
        }
×
785
        return config.Version
×
786
}
787

788
func (b CommandBuilder) Create(args []string) ([]*cli.Command, error) {
39✔
789
        version := b.parseArgument(args, versionFlagName)
39✔
790
        profile := b.parseArgument(args, profileFlagName)
39✔
791
        if version == "" && profile != "" {
39✔
792
                version = b.versionFromProfile(profile)
×
793
        }
×
794
        definitions, err := b.loadDefinitions(args, version)
39✔
795
        if err != nil {
39✔
796
                return nil, err
×
797
        }
×
798
        servicesCommands := b.createServiceCommands(definitions)
39✔
799
        autocompleteCommand := b.createAutoCompleteCommand(version)
39✔
800
        configCommand := b.createConfigCommand()
39✔
801
        showCommand := b.createShowCommand(definitions, servicesCommands)
39✔
802
        commands := append(servicesCommands, autocompleteCommand, configCommand, showCommand)
39✔
803
        return commands, nil
39✔
804
}
805

806
func (b CommandBuilder) CreateDefaultFlags(hidden bool) []cli.Flag {
945✔
807
        return []cli.Flag{
945✔
808
                &cli.BoolFlag{
945✔
809
                        Name:    debugFlagName,
945✔
810
                        Usage:   "Enable debug output",
945✔
811
                        EnvVars: []string{"UIPATH_DEBUG"},
945✔
812
                        Value:   false,
945✔
813
                        Hidden:  hidden,
945✔
814
                },
945✔
815
                &cli.StringFlag{
945✔
816
                        Name:    profileFlagName,
945✔
817
                        Usage:   "Config profile to use",
945✔
818
                        EnvVars: []string{"UIPATH_PROFILE"},
945✔
819
                        Value:   config.DefaultProfile,
945✔
820
                        Hidden:  hidden,
945✔
821
                },
945✔
822
                &cli.StringFlag{
945✔
823
                        Name:    uriFlagName,
945✔
824
                        Usage:   "Server Base-URI",
945✔
825
                        EnvVars: []string{"UIPATH_URI"},
945✔
826
                        Hidden:  hidden,
945✔
827
                },
945✔
828
                &cli.StringFlag{
945✔
829
                        Name:    organizationFlagName,
945✔
830
                        Usage:   "Organization name",
945✔
831
                        EnvVars: []string{"UIPATH_ORGANIZATION"},
945✔
832
                        Hidden:  hidden,
945✔
833
                },
945✔
834
                &cli.StringFlag{
945✔
835
                        Name:    tenantFlagName,
945✔
836
                        Usage:   "Tenant name",
945✔
837
                        EnvVars: []string{"UIPATH_TENANT"},
945✔
838
                        Hidden:  hidden,
945✔
839
                },
945✔
840
                &cli.BoolFlag{
945✔
841
                        Name:    insecureFlagName,
945✔
842
                        Usage:   "Disable HTTPS certificate check",
945✔
843
                        EnvVars: []string{"UIPATH_INSECURE"},
945✔
844
                        Value:   false,
945✔
845
                        Hidden:  hidden,
945✔
846
                },
945✔
847
                &cli.StringFlag{
945✔
848
                        Name:    outputFormatFlagName,
945✔
849
                        Usage:   fmt.Sprintf("Set output format: %s (default), %s", outputFormatJson, outputFormatText),
945✔
850
                        EnvVars: []string{"UIPATH_OUTPUT"},
945✔
851
                        Value:   "",
945✔
852
                        Hidden:  hidden,
945✔
853
                },
945✔
854
                &cli.StringFlag{
945✔
855
                        Name:   queryFlagName,
945✔
856
                        Usage:  "Perform JMESPath query on output",
945✔
857
                        Value:  "",
945✔
858
                        Hidden: hidden,
945✔
859
                },
945✔
860
                &cli.StringFlag{
945✔
861
                        Name:   waitFlagName,
945✔
862
                        Usage:  "Waits for the provided condition (JMESPath expression)",
945✔
863
                        Value:  "",
945✔
864
                        Hidden: hidden,
945✔
865
                },
945✔
866
                &cli.IntFlag{
945✔
867
                        Name:   waitTimeoutFlagName,
945✔
868
                        Usage:  "Time to wait in seconds for condition",
945✔
869
                        Value:  30,
945✔
870
                        Hidden: hidden,
945✔
871
                },
945✔
872
                &cli.StringFlag{
945✔
873
                        Name:   fileFlagName,
945✔
874
                        Usage:  "Provide input from file (use - for stdin)",
945✔
875
                        Value:  "",
945✔
876
                        Hidden: hidden,
945✔
877
                },
945✔
878
                b.VersionFlag(hidden),
945✔
879
        }
945✔
880
}
945✔
881

882
func (b CommandBuilder) VersionFlag(hidden bool) cli.Flag {
1,158✔
883
        return &cli.StringFlag{
1,158✔
884
                Name:    versionFlagName,
1,158✔
885
                Usage:   "Specific service version",
1,158✔
886
                EnvVars: []string{"UIPATH_VERSION"},
1,158✔
887
                Value:   "",
1,158✔
888
                Hidden:  hidden,
1,158✔
889
        }
1,158✔
890
}
1,158✔
891

892
func (b CommandBuilder) HelpFlag() cli.Flag {
1,392✔
893
        return &cli.BoolFlag{
1,392✔
894
                Name:   helpFlagName,
1,392✔
895
                Usage:  "Show help",
1,392✔
896
                Value:  false,
1,392✔
897
                Hidden: true,
1,392✔
898
        }
1,392✔
899
}
1,392✔
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