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

UiPath / uipathcli / 9016906752

09 May 2024 12:00PM UTC coverage: 90.387% (-0.05%) from 90.435%
9016906752

push

github

web-flow
Merge pull request #115 from UiPath/feature/identity-uri

Add global parameter identity-uri

43 of 47 new or added lines in 10 files covered. (91.49%)

4 existing lines in 1 file now uncovered.

4137 of 4577 relevant lines covered (90.39%)

1.02 hits per line

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

95.88
/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 identityUriFlagName = "identity-uri"
30
const organizationFlagName = "organization"
31
const tenantFlagName = "tenant"
32
const helpFlagName = "help"
33
const outputFormatFlagName = "output"
34
const queryFlagName = "query"
35
const waitFlagName = "wait"
36
const waitTimeoutFlagName = "wait-timeout"
37
const versionFlagName = "version"
38
const fileFlagName = "file"
39

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

57
const outputFormatJson = "json"
58
const outputFormatText = "text"
59

60
const subcommandHelpTemplate = `NAME:
61
   {{template "helpNameTemplate" .}}
62

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

66
DESCRIPTION:
67
   {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}}
68

69
COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
70

71
OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
72

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

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

90
func (b CommandBuilder) sort(commands []*cli.Command) {
1✔
91
        sort.Slice(commands, func(i, j int) bool {
2✔
92
                return commands[i].Name < commands[j].Name
1✔
93
        })
1✔
94
}
95

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

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

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

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

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

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

187
func (b CommandBuilder) sortParameters(parameters []parser.Parameter) {
1✔
188
        sort.Slice(parameters, func(i, j int) bool {
2✔
189
                if parameters[i].Required && !parameters[j].Required {
2✔
190
                        return true
1✔
191
                }
1✔
192
                if !parameters[i].Required && parameters[j].Required {
2✔
193
                        return false
1✔
194
                }
1✔
195
                return parameters[i].Name < parameters[j].Name
1✔
196
        })
197
}
198

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

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

219
        builder := NewUriBuilder(operation.BaseUri)
1✔
220
        builder.OverrideUri(config.Uri)
1✔
221
        builder.OverrideUri(uriArgument)
1✔
222
        return builder.Uri(), nil
1✔
223
}
224

225
func (b CommandBuilder) createIdentityUri(context *cli.Context, config config.Config, baseUri url.URL) (*url.URL, error) {
1✔
226
        uri := context.String(identityUriFlagName)
1✔
227
        if uri != "" {
2✔
228
                identityUri, err := url.Parse(uri)
1✔
229
                if err != nil {
2✔
230
                        return nil, fmt.Errorf("Error parsing %s argument: %w", identityUriFlagName, err)
1✔
231
                }
1✔
NEW
232
                return identityUri, nil
×
233
        }
234

235
        value := config.Auth.Config["uri"]
1✔
236
        uri, valid := value.(string)
1✔
237
        if valid && uri != "" {
2✔
238
                identityUri, err := url.Parse(uri)
1✔
239
                if err != nil {
2✔
240
                        return nil, fmt.Errorf("Error parsing identity uri config: %w", err)
1✔
241
                }
1✔
NEW
242
                return identityUri, nil
×
243
        }
244
        identityUri, err := url.Parse(fmt.Sprintf("%s://%s/identity_", baseUri.Scheme, baseUri.Host))
1✔
245
        if err != nil {
1✔
NEW
246
                return nil, fmt.Errorf("Error parsing identity uri: %w", err)
×
NEW
247
        }
×
248
        return identityUri, nil
1✔
249
}
250

251
func (b CommandBuilder) parseUriArgument(context *cli.Context) (*url.URL, error) {
1✔
252
        uriFlag := context.String(uriFlagName)
1✔
253
        if uriFlag == "" {
2✔
254
                return nil, nil
1✔
255
        }
1✔
256
        uriArgument, err := url.Parse(uriFlag)
1✔
257
        if err != nil {
1✔
258
                return nil, fmt.Errorf("Error parsing %s argument: %w", uriFlagName, err)
×
259
        }
×
260
        return uriArgument, nil
1✔
261
}
262

263
func (b CommandBuilder) getValue(parameter parser.Parameter, context *cli.Context, config config.Config) string {
1✔
264
        value := context.String(parameter.Name)
1✔
265
        if value != "" {
2✔
266
                return value
1✔
267
        }
1✔
268
        value = config.Parameter[parameter.Name]
1✔
269
        if value != "" {
2✔
270
                return value
1✔
271
        }
1✔
272
        value = config.Header[parameter.Name]
1✔
273
        if value != "" {
1✔
274
                return value
×
275
        }
×
276
        if parameter.DefaultValue != nil {
2✔
277
                return fmt.Sprintf("%v", parameter.DefaultValue)
1✔
278
        }
1✔
279
        return ""
1✔
280
}
281

282
func (b CommandBuilder) validateArguments(context *cli.Context, parameters []parser.Parameter, config config.Config) error {
1✔
283
        err := errors.New("Invalid arguments:")
1✔
284
        result := true
1✔
285
        for _, parameter := range parameters {
2✔
286
                value := b.getValue(parameter, context, config)
1✔
287
                if parameter.Required && value == "" {
2✔
288
                        result = false
1✔
289
                        err = fmt.Errorf("%w\n  Argument --%s is missing", err, parameter.Name)
1✔
290
                }
1✔
291
                if value != "" && len(parameter.AllowedValues) > 0 {
2✔
292
                        valid := false
1✔
293
                        for _, allowedValue := range parameter.AllowedValues {
2✔
294
                                if fmt.Sprintf("%v", allowedValue) == value {
2✔
295
                                        valid = true
1✔
296
                                        break
1✔
297
                                }
298
                        }
299
                        if !valid {
2✔
300
                                allowedValues := b.formatAllowedValues(parameter.AllowedValues)
1✔
301
                                result = false
1✔
302
                                err = fmt.Errorf("%w\n  Argument value '%v' for --%s is invalid, allowed values: %s", err, value, parameter.Name, allowedValues)
1✔
303
                        }
1✔
304
                }
305
        }
306
        if result {
2✔
307
                return nil
1✔
308
        }
1✔
309
        return err
1✔
310
}
311

312
func (b CommandBuilder) logger(context executor.ExecutionContext, writer io.Writer) log.Logger {
1✔
313
        if context.Debug {
2✔
314
                return log.NewDebugLogger(writer)
1✔
315
        }
1✔
316
        return log.NewDefaultLogger(writer)
1✔
317
}
318

319
func (b CommandBuilder) outputWriter(writer io.Writer, format string, query string) output.OutputWriter {
1✔
320
        var transformer output.Transformer = output.NewDefaultTransformer()
1✔
321
        if query != "" {
2✔
322
                transformer = output.NewJmesPathTransformer(query)
1✔
323
        }
1✔
324
        if format == outputFormatText {
2✔
325
                return output.NewTextOutputWriter(writer, transformer)
1✔
326
        }
1✔
327
        return output.NewJsonOutputWriter(writer, transformer)
1✔
328
}
329

330
func (b CommandBuilder) executeCommand(context executor.ExecutionContext, writer output.OutputWriter, logger log.Logger) error {
1✔
331
        if context.Plugin != nil {
2✔
332
                return b.PluginExecutor.Call(context, writer, logger)
1✔
333
        }
1✔
334
        return b.Executor.Call(context, writer, logger)
1✔
335
}
336

337
func (b CommandBuilder) createOperationCommand(operation parser.Operation) *cli.Command {
1✔
338
        parameters := operation.Parameters
1✔
339
        b.sortParameters(parameters)
1✔
340

1✔
341
        flagBuilder := newFlagBuilder()
1✔
342
        flagBuilder.AddFlags(b.createFlags(parameters))
1✔
343
        flagBuilder.AddFlags(b.CreateDefaultFlags(true))
1✔
344
        flagBuilder.AddFlag(b.HelpFlag())
1✔
345

1✔
346
        return &cli.Command{
1✔
347
                Name:               operation.Name,
1✔
348
                Usage:              operation.Summary,
1✔
349
                Description:        operation.Description,
1✔
350
                Flags:              flagBuilder.ToList(),
1✔
351
                CustomHelpTemplate: subcommandHelpTemplate,
1✔
352
                Action: func(context *cli.Context) error {
2✔
353
                        profileName := context.String(profileFlagName)
1✔
354
                        config := b.ConfigProvider.Config(profileName)
1✔
355
                        if config == nil {
2✔
356
                                return fmt.Errorf("Could not find profile '%s'", profileName)
1✔
357
                        }
1✔
358
                        outputFormat, err := b.outputFormat(*config, context)
1✔
359
                        if err != nil {
1✔
360
                                return err
×
361
                        }
×
362
                        query := context.String(queryFlagName)
1✔
363
                        wait := context.String(waitFlagName)
1✔
364
                        waitTimeout := context.Int(waitTimeoutFlagName)
1✔
365

1✔
366
                        baseUri, err := b.createBaseUri(operation, *config, context)
1✔
367
                        if err != nil {
1✔
368
                                return err
×
369
                        }
×
370

371
                        input := b.fileInput(context, operation.Parameters)
1✔
372
                        if input == nil {
2✔
373
                                err = b.validateArguments(context, operation.Parameters, *config)
1✔
374
                                if err != nil {
2✔
375
                                        return err
1✔
376
                                }
1✔
377
                        }
378

379
                        parameters, err := b.createExecutionParameters(context, config, operation)
1✔
380
                        if err != nil {
2✔
381
                                return err
1✔
382
                        }
1✔
383

384
                        organization := context.String(organizationFlagName)
1✔
385
                        if organization == "" {
2✔
386
                                organization = config.Organization
1✔
387
                        }
1✔
388
                        tenant := context.String(tenantFlagName)
1✔
389
                        if tenant == "" {
2✔
390
                                tenant = config.Tenant
1✔
391
                        }
1✔
392
                        insecure := context.Bool(insecureFlagName) || config.Insecure
1✔
393
                        debug := context.Bool(debugFlagName) || config.Debug
1✔
394
                        identityUri, err := b.createIdentityUri(context, *config, baseUri)
1✔
395
                        if err != nil {
2✔
396
                                return err
1✔
397
                        }
1✔
398

399
                        executionContext := executor.NewExecutionContext(
1✔
400
                                organization,
1✔
401
                                tenant,
1✔
402
                                operation.Method,
1✔
403
                                baseUri,
1✔
404
                                operation.Route,
1✔
405
                                operation.ContentType,
1✔
406
                                input,
1✔
407
                                parameters,
1✔
408
                                config.Auth,
1✔
409
                                insecure,
1✔
410
                                debug,
1✔
411
                                *identityUri,
1✔
412
                                operation.Plugin)
1✔
413

1✔
414
                        if wait != "" {
2✔
415
                                return b.executeWait(*executionContext, outputFormat, query, wait, waitTimeout)
1✔
416
                        }
1✔
417
                        return b.execute(*executionContext, outputFormat, query, nil)
1✔
418
                },
419
                HideHelp: true,
420
                Hidden:   operation.Hidden,
421
        }
422
}
423

424
func (b CommandBuilder) executeWait(executionContext executor.ExecutionContext, outputFormat string, query string, wait string, waitTimeout int) error {
1✔
425
        logger := log.NewDefaultLogger(b.StdErr)
1✔
426
        outputWriter := output.NewMemoryOutputWriter()
1✔
427
        for start := time.Now(); time.Since(start) < time.Duration(waitTimeout)*time.Second; {
2✔
428
                err := b.execute(executionContext, "json", "", outputWriter)
1✔
429
                result, evaluationErr := b.evaluateWaitCondition(outputWriter.Response(), wait)
1✔
430
                if evaluationErr != nil {
2✔
431
                        return evaluationErr
1✔
432
                }
1✔
433
                if result {
2✔
434
                        resultWriter := b.outputWriter(b.StdOut, outputFormat, query)
1✔
435
                        _ = resultWriter.WriteResponse(outputWriter.Response())
1✔
436
                        return err
1✔
437
                }
1✔
438
                logger.LogError("Condition is not met yet. Waiting...\n")
1✔
439
                time.Sleep(1 * time.Second)
1✔
440
        }
441
        return errors.New("Timed out waiting for condition")
1✔
442
}
443

444
func (b CommandBuilder) evaluateWaitCondition(response output.ResponseInfo, wait string) (bool, error) {
1✔
445
        body, err := io.ReadAll(response.Body)
1✔
446
        if err != nil {
1✔
447
                return false, nil
×
448
        }
×
449
        var data interface{}
1✔
450
        err = json.Unmarshal(body, &data)
1✔
451
        if err != nil {
1✔
452
                return false, nil
×
453
        }
×
454
        transformer := output.NewJmesPathTransformer(wait)
1✔
455
        result, err := transformer.Execute(data)
1✔
456
        if err != nil {
2✔
457
                return false, err
1✔
458
        }
1✔
459
        value, ok := result.(bool)
1✔
460
        if !ok {
2✔
461
                return false, fmt.Errorf("Error in wait condition: JMESPath expression needs to return boolean")
1✔
462
        }
1✔
463
        return value, nil
1✔
464
}
465

466
func (b CommandBuilder) execute(executionContext executor.ExecutionContext, outputFormat string, query string, outputWriter output.OutputWriter) error {
1✔
467
        var wg sync.WaitGroup
1✔
468
        wg.Add(3)
1✔
469
        reader, writer := io.Pipe()
1✔
470
        go func() {
2✔
471
                defer wg.Done()
1✔
472
                defer reader.Close()
1✔
473
                _, _ = io.Copy(b.StdOut, reader)
1✔
474
        }()
1✔
475
        errorReader, errorWriter := io.Pipe()
1✔
476
        go func() {
2✔
477
                defer wg.Done()
1✔
478
                defer errorReader.Close()
1✔
479
                _, _ = io.Copy(b.StdErr, errorReader)
1✔
480
        }()
1✔
481

482
        var err error
1✔
483
        go func() {
2✔
484
                defer wg.Done()
1✔
485
                defer writer.Close()
1✔
486
                defer errorWriter.Close()
1✔
487
                if outputWriter == nil {
2✔
488
                        outputWriter = b.outputWriter(writer, outputFormat, query)
1✔
489
                }
1✔
490
                logger := b.logger(executionContext, errorWriter)
1✔
491
                err = b.executeCommand(executionContext, outputWriter, logger)
1✔
492
        }()
493

494
        wg.Wait()
1✔
495
        return err
1✔
496
}
497

498
func (b CommandBuilder) createCategoryCommand(operation parser.Operation) *cli.Command {
1✔
499
        return &cli.Command{
1✔
500
                Name:        operation.Category.Name,
1✔
501
                Description: operation.Category.Description,
1✔
502
                Flags: []cli.Flag{
1✔
503
                        b.HelpFlag(),
1✔
504
                        b.VersionFlag(true),
1✔
505
                },
1✔
506
                HideHelp: true,
1✔
507
        }
1✔
508
}
1✔
509

510
func (b CommandBuilder) createServiceCommandCategory(operation parser.Operation, categories map[string]*cli.Command) (bool, *cli.Command) {
1✔
511
        isNewCategory := false
1✔
512
        operationCommand := b.createOperationCommand(operation)
1✔
513
        command, found := categories[operation.Category.Name]
1✔
514
        if !found {
2✔
515
                command = b.createCategoryCommand(operation)
1✔
516
                categories[operation.Category.Name] = command
1✔
517
                isNewCategory = true
1✔
518
        }
1✔
519
        command.Subcommands = append(command.Subcommands, operationCommand)
1✔
520
        return isNewCategory, command
1✔
521
}
522

523
func (b CommandBuilder) createServiceCommand(definition parser.Definition) *cli.Command {
1✔
524
        categories := map[string]*cli.Command{}
1✔
525
        commands := []*cli.Command{}
1✔
526
        for _, operation := range definition.Operations {
2✔
527
                if operation.Category == nil {
2✔
528
                        command := b.createOperationCommand(operation)
1✔
529
                        commands = append(commands, command)
1✔
530
                        continue
1✔
531
                }
532
                isNewCategory, command := b.createServiceCommandCategory(operation, categories)
1✔
533
                if isNewCategory {
2✔
534
                        commands = append(commands, command)
1✔
535
                }
1✔
536
        }
537
        b.sort(commands)
1✔
538
        for _, command := range commands {
2✔
539
                b.sort(command.Subcommands)
1✔
540
        }
1✔
541

542
        return &cli.Command{
1✔
543
                Name:        definition.Name,
1✔
544
                Description: definition.Description,
1✔
545
                Flags: []cli.Flag{
1✔
546
                        b.HelpFlag(),
1✔
547
                        b.VersionFlag(true),
1✔
548
                },
1✔
549
                Subcommands: commands,
1✔
550
                HideHelp:    true,
1✔
551
        }
1✔
552
}
553

554
func (b CommandBuilder) createAutoCompleteEnableCommand() *cli.Command {
1✔
555
        const shellFlagName = "shell"
1✔
556
        const powershellFlagValue = "powershell"
1✔
557
        const bashFlagValue = "bash"
1✔
558
        const fileFlagName = "file"
1✔
559

1✔
560
        return &cli.Command{
1✔
561
                Name:        "enable",
1✔
562
                Description: "Enables auto complete in your shell",
1✔
563
                Flags: []cli.Flag{
1✔
564
                        &cli.StringFlag{
1✔
565
                                Name:     shellFlagName,
1✔
566
                                Usage:    fmt.Sprintf("%s, %s", powershellFlagValue, bashFlagValue),
1✔
567
                                Required: true,
1✔
568
                        },
1✔
569
                        &cli.StringFlag{
1✔
570
                                Name:   fileFlagName,
1✔
571
                                Hidden: true,
1✔
572
                        },
1✔
573
                        b.HelpFlag(),
1✔
574
                },
1✔
575
                Action: func(context *cli.Context) error {
2✔
576
                        shell := context.String(shellFlagName)
1✔
577
                        filePath := context.String(fileFlagName)
1✔
578
                        handler := newAutoCompleteHandler()
1✔
579
                        output, err := handler.EnableCompleter(shell, filePath)
1✔
580
                        if err != nil {
2✔
581
                                return err
1✔
582
                        }
1✔
583
                        fmt.Fprintln(b.StdOut, output)
1✔
584
                        return nil
1✔
585
                },
586
                HideHelp: true,
587
        }
588
}
589

590
func (b CommandBuilder) createAutoCompleteCompleteCommand(version string) *cli.Command {
1✔
591
        return &cli.Command{
1✔
592
                Name:        "complete",
1✔
593
                Description: "Returns the autocomplete suggestions",
1✔
594
                Flags: []cli.Flag{
1✔
595
                        &cli.StringFlag{
1✔
596
                                Name:     "command",
1✔
597
                                Usage:    "The command to autocomplete",
1✔
598
                                Required: true,
1✔
599
                        },
1✔
600
                        b.HelpFlag(),
1✔
601
                },
1✔
602
                Action: func(context *cli.Context) error {
2✔
603
                        commandText := context.String("command")
1✔
604
                        exclude := []string{}
1✔
605
                        for _, flagName := range predefinedFlags {
2✔
606
                                exclude = append(exclude, "--"+flagName)
1✔
607
                        }
1✔
608
                        args := strings.Split(commandText, " ")
1✔
609
                        definitions, err := b.loadAutocompleteDefinitions(args, version)
1✔
610
                        if err != nil {
1✔
611
                                return err
×
612
                        }
×
613
                        commands := b.createServiceCommands(definitions)
1✔
614
                        handler := newAutoCompleteHandler()
1✔
615
                        words := handler.Find(commandText, commands, exclude)
1✔
616
                        for _, word := range words {
2✔
617
                                fmt.Fprintln(b.StdOut, word)
1✔
618
                        }
1✔
619
                        return nil
1✔
620
                },
621
                HideHelp: true,
622
        }
623
}
624

625
func (b CommandBuilder) createAutoCompleteCommand(version string) *cli.Command {
1✔
626
        return &cli.Command{
1✔
627
                Name:        "autocomplete",
1✔
628
                Description: "Commands for autocompletion",
1✔
629
                Flags: []cli.Flag{
1✔
630
                        b.HelpFlag(),
1✔
631
                },
1✔
632
                Subcommands: []*cli.Command{
1✔
633
                        b.createAutoCompleteEnableCommand(),
1✔
634
                        b.createAutoCompleteCompleteCommand(version),
1✔
635
                },
1✔
636
                HideHelp: true,
1✔
637
        }
1✔
638
}
1✔
639

640
func (b CommandBuilder) createConfigCommand() *cli.Command {
1✔
641
        authFlagName := "auth"
1✔
642
        flags := []cli.Flag{
1✔
643
                &cli.StringFlag{
1✔
644
                        Name:  authFlagName,
1✔
645
                        Usage: fmt.Sprintf("Authorization type: %s, %s, %s", CredentialsAuth, LoginAuth, PatAuth),
1✔
646
                },
1✔
647
                &cli.StringFlag{
1✔
648
                        Name:    profileFlagName,
1✔
649
                        Usage:   "Profile to configure",
1✔
650
                        EnvVars: []string{"UIPATH_PROFILE"},
1✔
651
                        Value:   config.DefaultProfile,
1✔
652
                },
1✔
653
                b.HelpFlag(),
1✔
654
        }
1✔
655

1✔
656
        return &cli.Command{
1✔
657
                Name:        "config",
1✔
658
                Description: "Interactive command to configure the CLI",
1✔
659
                Flags:       flags,
1✔
660
                Subcommands: []*cli.Command{
1✔
661
                        b.createConfigSetCommand(),
1✔
662
                },
1✔
663
                Action: func(context *cli.Context) error {
2✔
664
                        auth := context.String(authFlagName)
1✔
665
                        profileName := context.String(profileFlagName)
1✔
666
                        handler := ConfigCommandHandler{
1✔
667
                                StdIn:          b.StdIn,
1✔
668
                                StdOut:         b.StdOut,
1✔
669
                                ConfigProvider: b.ConfigProvider,
1✔
670
                        }
1✔
671
                        return handler.Configure(auth, profileName)
1✔
672
                },
1✔
673
                HideHelp: true,
674
        }
675
}
676

677
func (b CommandBuilder) createConfigSetCommand() *cli.Command {
1✔
678
        keyFlagName := "key"
1✔
679
        valueFlagName := "value"
1✔
680
        flags := []cli.Flag{
1✔
681
                &cli.StringFlag{
1✔
682
                        Name:     keyFlagName,
1✔
683
                        Usage:    "The key",
1✔
684
                        Required: true,
1✔
685
                },
1✔
686
                &cli.StringFlag{
1✔
687
                        Name:     valueFlagName,
1✔
688
                        Usage:    "The value to set",
1✔
689
                        Required: true,
1✔
690
                },
1✔
691
                &cli.StringFlag{
1✔
692
                        Name:    profileFlagName,
1✔
693
                        Usage:   "Profile to configure",
1✔
694
                        EnvVars: []string{"UIPATH_PROFILE"},
1✔
695
                        Value:   config.DefaultProfile,
1✔
696
                },
1✔
697
                b.HelpFlag(),
1✔
698
        }
1✔
699
        return &cli.Command{
1✔
700
                Name:        "set",
1✔
701
                Description: "Set config parameters",
1✔
702
                Flags:       flags,
1✔
703
                Action: func(context *cli.Context) error {
2✔
704
                        profileName := context.String(profileFlagName)
1✔
705
                        key := context.String(keyFlagName)
1✔
706
                        value := context.String(valueFlagName)
1✔
707
                        handler := ConfigCommandHandler{
1✔
708
                                StdIn:          b.StdIn,
1✔
709
                                StdOut:         b.StdOut,
1✔
710
                                ConfigProvider: b.ConfigProvider,
1✔
711
                        }
1✔
712
                        return handler.Set(key, value, profileName)
1✔
713
                },
1✔
714
                HideHelp: true,
715
        }
716
}
717

718
func (b CommandBuilder) loadDefinitions(args []string, version string) ([]parser.Definition, error) {
1✔
719
        if len(args) <= 1 || strings.HasPrefix(args[1], "--") {
2✔
720
                return b.DefinitionProvider.Index(version)
1✔
721
        }
1✔
722
        if len(args) > 1 && args[1] == "commands" {
2✔
723
                return b.loadAllDefinitions(version)
1✔
724
        }
1✔
725
        definition, err := b.DefinitionProvider.Load(args[1], version)
1✔
726
        if definition == nil {
2✔
727
                return nil, err
1✔
728
        }
1✔
729
        return []parser.Definition{*definition}, err
1✔
730
}
731

732
func (b CommandBuilder) loadAllDefinitions(version string) ([]parser.Definition, error) {
1✔
733
        all, err := b.DefinitionProvider.Index(version)
1✔
734
        if err != nil {
1✔
735
                return nil, err
×
736
        }
×
737
        definitions := []parser.Definition{}
1✔
738
        for _, d := range all {
2✔
739
                definition, err := b.DefinitionProvider.Load(d.Name, version)
1✔
740
                if err != nil {
1✔
741
                        return nil, err
×
742
                }
×
743
                if definition != nil {
2✔
744
                        definitions = append(definitions, *definition)
1✔
745
                }
1✔
746
        }
747
        return definitions, nil
1✔
748
}
749

750
func (b CommandBuilder) loadAutocompleteDefinitions(args []string, version string) ([]parser.Definition, error) {
1✔
751
        if len(args) <= 2 {
2✔
752
                return b.DefinitionProvider.Index(version)
1✔
753
        }
1✔
754
        return b.loadDefinitions(args, version)
1✔
755
}
756

757
func (b CommandBuilder) createShowCommand(definitions []parser.Definition, commands []*cli.Command) *cli.Command {
1✔
758
        return &cli.Command{
1✔
759
                Name:        "commands",
1✔
760
                Description: "Command to inspect available uipath CLI operations",
1✔
761
                Flags: []cli.Flag{
1✔
762
                        b.HelpFlag(),
1✔
763
                },
1✔
764
                Subcommands: []*cli.Command{
1✔
765
                        {
1✔
766
                                Name:        "show",
1✔
767
                                Description: "Print available uipath CLI commands",
1✔
768
                                Flags: []cli.Flag{
1✔
769
                                        b.HelpFlag(),
1✔
770
                                },
1✔
771
                                Action: func(context *cli.Context) error {
2✔
772
                                        flagBuilder := newFlagBuilder()
1✔
773
                                        flagBuilder.AddFlags(b.CreateDefaultFlags(false))
1✔
774
                                        flagBuilder.AddFlag(b.HelpFlag())
1✔
775
                                        flags := flagBuilder.ToList()
1✔
776

1✔
777
                                        handler := newShowCommandHandler()
1✔
778
                                        output, err := handler.Execute(definitions, flags)
1✔
779
                                        if err != nil {
1✔
780
                                                return err
×
781
                                        }
×
782
                                        fmt.Fprintln(b.StdOut, output)
1✔
783
                                        return nil
1✔
784
                                },
785
                                HideHelp: true,
786
                                Hidden:   true,
787
                        },
788
                },
789
                HideHelp: true,
790
                Hidden:   true,
791
        }
792
}
793

794
func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*cli.Command {
1✔
795
        commands := []*cli.Command{}
1✔
796
        for _, e := range definitions {
2✔
797
                command := b.createServiceCommand(e)
1✔
798
                commands = append(commands, command)
1✔
799
        }
1✔
800
        return commands
1✔
801
}
802

803
func (b CommandBuilder) parseArgument(args []string, name string) string {
1✔
804
        for i, arg := range args {
2✔
805
                if strings.TrimSpace(arg) == "--"+name {
2✔
806
                        if len(args) > i+1 {
2✔
807
                                return strings.TrimSpace(args[i+1])
1✔
808
                        }
1✔
809
                }
810
        }
811
        return ""
1✔
812
}
813

814
func (b CommandBuilder) versionFromProfile(profile string) string {
1✔
815
        config := b.ConfigProvider.Config(profile)
1✔
816
        if config == nil {
2✔
817
                return ""
1✔
818
        }
1✔
819
        return config.Version
1✔
820
}
821

822
func (b CommandBuilder) Create(args []string) ([]*cli.Command, error) {
1✔
823
        version := b.parseArgument(args, versionFlagName)
1✔
824
        profile := b.parseArgument(args, profileFlagName)
1✔
825
        if version == "" && profile != "" {
2✔
826
                version = b.versionFromProfile(profile)
1✔
827
        }
1✔
828
        definitions, err := b.loadDefinitions(args, version)
1✔
829
        if err != nil {
2✔
830
                return nil, err
1✔
831
        }
1✔
832
        servicesCommands := b.createServiceCommands(definitions)
1✔
833
        autocompleteCommand := b.createAutoCompleteCommand(version)
1✔
834
        configCommand := b.createConfigCommand()
1✔
835
        showCommand := b.createShowCommand(definitions, servicesCommands)
1✔
836
        commands := append(servicesCommands, autocompleteCommand, configCommand, showCommand)
1✔
837
        return commands, nil
1✔
838
}
839

840
func (b CommandBuilder) CreateDefaultFlags(hidden bool) []cli.Flag {
1✔
841
        return []cli.Flag{
1✔
842
                &cli.BoolFlag{
1✔
843
                        Name:    debugFlagName,
1✔
844
                        Usage:   "Enable debug output",
1✔
845
                        EnvVars: []string{"UIPATH_DEBUG"},
1✔
846
                        Value:   false,
1✔
847
                        Hidden:  hidden,
1✔
848
                },
1✔
849
                &cli.StringFlag{
1✔
850
                        Name:    profileFlagName,
1✔
851
                        Usage:   "Config profile to use",
1✔
852
                        EnvVars: []string{"UIPATH_PROFILE"},
1✔
853
                        Value:   config.DefaultProfile,
1✔
854
                        Hidden:  hidden,
1✔
855
                },
1✔
856
                &cli.StringFlag{
1✔
857
                        Name:    uriFlagName,
1✔
858
                        Usage:   "Server Base-URI",
1✔
859
                        EnvVars: []string{"UIPATH_URI"},
1✔
860
                        Hidden:  hidden,
1✔
861
                },
1✔
862
                &cli.StringFlag{
1✔
863
                        Name:    organizationFlagName,
1✔
864
                        Usage:   "Organization name",
1✔
865
                        EnvVars: []string{"UIPATH_ORGANIZATION"},
1✔
866
                        Hidden:  hidden,
1✔
867
                },
1✔
868
                &cli.StringFlag{
1✔
869
                        Name:    tenantFlagName,
1✔
870
                        Usage:   "Tenant name",
1✔
871
                        EnvVars: []string{"UIPATH_TENANT"},
1✔
872
                        Hidden:  hidden,
1✔
873
                },
1✔
874
                &cli.BoolFlag{
1✔
875
                        Name:    insecureFlagName,
1✔
876
                        Usage:   "Disable HTTPS certificate check",
1✔
877
                        EnvVars: []string{"UIPATH_INSECURE"},
1✔
878
                        Value:   false,
1✔
879
                        Hidden:  hidden,
1✔
880
                },
1✔
881
                &cli.StringFlag{
1✔
882
                        Name:    outputFormatFlagName,
1✔
883
                        Usage:   fmt.Sprintf("Set output format: %s (default), %s", outputFormatJson, outputFormatText),
1✔
884
                        EnvVars: []string{"UIPATH_OUTPUT"},
1✔
885
                        Value:   "",
1✔
886
                        Hidden:  hidden,
1✔
887
                },
1✔
888
                &cli.StringFlag{
1✔
889
                        Name:   queryFlagName,
1✔
890
                        Usage:  "Perform JMESPath query on output",
1✔
891
                        Value:  "",
1✔
892
                        Hidden: hidden,
1✔
893
                },
1✔
894
                &cli.StringFlag{
1✔
895
                        Name:   waitFlagName,
1✔
896
                        Usage:  "Waits for the provided condition (JMESPath expression)",
1✔
897
                        Value:  "",
1✔
898
                        Hidden: hidden,
1✔
899
                },
1✔
900
                &cli.IntFlag{
1✔
901
                        Name:   waitTimeoutFlagName,
1✔
902
                        Usage:  "Time to wait in seconds for condition",
1✔
903
                        Value:  30,
1✔
904
                        Hidden: hidden,
1✔
905
                },
1✔
906
                &cli.StringFlag{
1✔
907
                        Name:   fileFlagName,
1✔
908
                        Usage:  "Provide input from file (use - for stdin)",
1✔
909
                        Value:  "",
1✔
910
                        Hidden: hidden,
1✔
911
                },
1✔
912
                &cli.StringFlag{
1✔
913
                        Name:    identityUriFlagName,
1✔
914
                        Usage:   "Identity Server URI",
1✔
915
                        EnvVars: []string{"UIPATH_IDENTITY_URI"},
1✔
916
                        Hidden:  hidden,
1✔
917
                },
1✔
918
                b.VersionFlag(hidden),
1✔
919
        }
1✔
920
}
1✔
921

922
func (b CommandBuilder) VersionFlag(hidden bool) cli.Flag {
1✔
923
        return &cli.StringFlag{
1✔
924
                Name:    versionFlagName,
1✔
925
                Usage:   "Specific service version",
1✔
926
                EnvVars: []string{"UIPATH_VERSION"},
1✔
927
                Value:   "",
1✔
928
                Hidden:  hidden,
1✔
929
        }
1✔
930
}
1✔
931

932
func (b CommandBuilder) HelpFlag() cli.Flag {
1✔
933
        return &cli.BoolFlag{
1✔
934
                Name:   helpFlagName,
1✔
935
                Usage:  "Show help",
1✔
936
                Value:  false,
1✔
937
                Hidden: true,
1✔
938
        }
1✔
939
}
1✔
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