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

UiPath / uipathcli / 9013922507

09 May 2024 07:21AM UTC coverage: 90.205% (-0.2%) from 90.435%
9013922507

push

github

thschmitt
Add global parameter identity-uri

Added a new global parameter to override the identity server uri. This
makes it easier to use the UiPath services with an external identity
token provider. It was possible before to override the identity server
uri by setting the UIPATH_IDENTITY_URI environment variable but not by
providing a global CLI parameter.

Refactored the code so that the CLI parameter, config value and
environment variable parsing is done in the command_builder like all the
other parameters and removed the env variable parsing from the bearer
authenticator.

36 of 44 new or added lines in 8 files covered. (81.82%)

8 existing lines in 2 files now uncovered.

4144 of 4594 relevant lines covered (90.2%)

1.02 hits per line

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

95.34
/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 != "" {
1✔
NEW
228
                identityUri, err := url.Parse(uri)
×
NEW
229
                if err != nil {
×
NEW
230
                        return nil, fmt.Errorf("Error parsing %s argument: %w", identityUriFlagName, err)
×
NEW
231
                }
×
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