Correctly set ExactArgs if generated command has positional arguments (#488)

## Changes
Some of the command such as `databricks alerts create` require
positional arguments which are not primitive.

Since these arguments are required, we should correctly set ExactArgs
for such commands

Fixes #367 

## Tests
Running `databricks alerts create`

Before
```
andrew.nester@HFW9Y94129 cli % ./cli alerts create                     
panic: runtime error: index out of range [0] with length 0

goroutine 1 [running]:
github.com/databricks/bricks/cmd/workspace/alerts.glob..func1(0x22a1280?, {0x2321638, 0x0, 0x0?})
	github.com/databricks/bricks/cmd/workspace/alerts/alerts.go:57 +0x355
github.com/spf13/cobra.(*Command).execute(0x22a1280, {0x2321638, 0x0, 0x0})
	github.com/spf13/cobra@v1.7.0/command.go:940 +0x862
github.com/spf13/cobra.(*Command).ExecuteC(0x22a0700)
	github.com/spf13/cobra@v1.7.0/command.go:1068 +0x3bd
github.com/spf13/cobra.(*Command).ExecuteContextC(...)
	github.com/spf13/cobra@v1.7.0/command.go:1001
github.com/databricks/bricks/cmd/root.Execute()
	github.com/databricks/bricks/cmd/root/root.go:80 +0x6a
main.main()
	github.com/databricks/bricks/main.go:18 +0x17                                           

```

After
```
andrew.nester@HFW9Y94129 cli % ./cli alerts create                                                                
Error: provide command input in JSON format by specifying --json option
```
Acceptance test
```
=== RUN   TestAccAlertsCreateErrWhenNoArguments
    alerts_test.go:10: gcp
    helpers.go:147: Error running command: provide command input in JSON format by specifying --json option
--- PASS: TestAccAlertsCreateErrWhenNoArguments (1.99s)
PASS
```
This commit is contained in:
Andrew Nester 2023-06-16 14:15:25 +02:00 committed by GitHub
parent 18e1d93218
commit 9cf0e0db24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 59 additions and 239 deletions

View File

@ -133,6 +133,7 @@ var {{.CamelName}}Cmd = &cobra.Command{
}
{{- end -}}
{{$method := .}}
{{- if .Request.IsAllRequiredFieldsPrimitive -}}
{{- range $arg, $field := .Request.RequiredFields}}
{{if not $field.Entity.IsString -}}
_, err = fmt.Sscan(args[{{$arg}}], &{{$method.CamelName}}Req.{{$field.PascalName}})
@ -141,6 +142,9 @@ var {{.CamelName}}Cmd = &cobra.Command{
}{{else -}}
{{$method.CamelName}}Req.{{$field.PascalName}} = args[{{$arg}}]
{{- end -}}{{end}}
{{- else -}}
return fmt.Errorf("please provide command input in JSON format by specifying the --json flag")
{{- end -}}
}
{{end}}
{{if $wait -}}

View File

@ -55,10 +55,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &createReq.Budget)
if err != nil {
return fmt.Errorf("invalid BUDGET: %s", args[0])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.Budgets.Create(ctx, createReq)
@ -257,11 +254,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &updateReq.Budget)
if err != nil {
return fmt.Errorf("invalid BUDGET: %s", args[0])
}
updateReq.BudgetId = args[1]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = a.Budgets.Update(ctx, updateReq)

View File

@ -68,11 +68,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.CredentialsName = args[0]
_, err = fmt.Sscan(args[1], &createReq.AwsCredentials)
if err != nil {
return fmt.Errorf("invalid AWS_CREDENTIALS: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.Credentials.Create(ctx, createReq)

View File

@ -63,11 +63,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Name = args[0]
_, err = fmt.Sscan(args[1], &createReq.RedirectUrls)
if err != nil {
return fmt.Errorf("invalid REDIRECT_URLS: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.CustomAppIntegration.Create(ctx, createReq)

View File

@ -85,10 +85,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &createReq.UseCases)
if err != nil {
return fmt.Errorf("invalid USE_CASES: %s", args[0])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.EncryptionKeys.Create(ctx, createReq)

View File

@ -85,15 +85,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Label = args[0]
_, err = fmt.Sscan(args[1], &createReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &createReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.IpAccessLists.Create(ctx, createReq)
@ -300,20 +292,7 @@ var replaceCmd = &cobra.Command{
return err
}
} else {
replaceReq.Label = args[0]
_, err = fmt.Sscan(args[1], &replaceReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &replaceReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
_, err = fmt.Sscan(args[3], &replaceReq.Enabled)
if err != nil {
return fmt.Errorf("invalid ENABLED: %s", args[3])
}
replaceReq.IpAccessListId = args[4]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = a.IpAccessLists.Replace(ctx, replaceReq)
@ -372,20 +351,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
updateReq.Label = args[0]
_, err = fmt.Sscan(args[1], &updateReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &updateReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
_, err = fmt.Sscan(args[3], &updateReq.Enabled)
if err != nil {
return fmt.Errorf("invalid ENABLED: %s", args[3])
}
updateReq.IpAccessListId = args[4]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = a.IpAccessLists.Update(ctx, updateReq)

View File

@ -65,11 +65,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.StorageConfigurationName = args[0]
_, err = fmt.Sscan(args[1], &createReq.RootBucketInfo)
if err != nil {
return fmt.Errorf("invalid ROOT_BUCKET_INFO: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := a.Storage.Create(ctx, createReq)

View File

@ -221,18 +221,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &updateReq.Permissions)
if err != nil {
return fmt.Errorf("invalid PERMISSIONS: %s", args[0])
}
_, err = fmt.Sscan(args[1], &updateReq.WorkspaceId)
if err != nil {
return fmt.Errorf("invalid WORKSPACE_ID: %s", args[1])
}
_, err = fmt.Sscan(args[2], &updateReq.PrincipalId)
if err != nil {
return fmt.Errorf("invalid PRINCIPAL_ID: %s", args[2])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = a.WorkspaceAssignment.Update(ctx, updateReq)

View File

@ -60,12 +60,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Name = args[0]
_, err = fmt.Sscan(args[1], &createReq.Options)
if err != nil {
return fmt.Errorf("invalid OPTIONS: %s", args[1])
}
createReq.QueryId = args[2]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.Alerts.Create(ctx, createReq)
@ -265,13 +260,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
updateReq.Name = args[0]
_, err = fmt.Sscan(args[1], &updateReq.Options)
if err != nil {
return fmt.Errorf("invalid OPTIONS: %s", args[1])
}
updateReq.QueryId = args[2]
updateReq.AlertId = args[3]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.Alerts.Update(ctx, updateReq)

View File

@ -73,15 +73,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Name = args[0]
_, err = fmt.Sscan(args[1], &createReq.ConnectionType)
if err != nil {
return fmt.Errorf("invalid CONNECTION_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &createReq.OptionsKvpairs)
if err != nil {
return fmt.Errorf("invalid OPTIONS_KVPAIRS: %s", args[2])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.Connections.Create(ctx, createReq)
@ -277,12 +269,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
updateReq.Name = args[0]
_, err = fmt.Sscan(args[1], &updateReq.OptionsKvpairs)
if err != nil {
return fmt.Errorf("invalid OPTIONS_KVPAIRS: %s", args[1])
}
updateReq.NameArg = args[2]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.Connections.Update(ctx, updateReq)

View File

@ -66,52 +66,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Name = args[0]
createReq.CatalogName = args[1]
createReq.SchemaName = args[2]
_, err = fmt.Sscan(args[3], &createReq.InputParams)
if err != nil {
return fmt.Errorf("invalid INPUT_PARAMS: %s", args[3])
}
_, err = fmt.Sscan(args[4], &createReq.DataType)
if err != nil {
return fmt.Errorf("invalid DATA_TYPE: %s", args[4])
}
createReq.FullDataType = args[5]
_, err = fmt.Sscan(args[6], &createReq.ReturnParams)
if err != nil {
return fmt.Errorf("invalid RETURN_PARAMS: %s", args[6])
}
_, err = fmt.Sscan(args[7], &createReq.RoutineBody)
if err != nil {
return fmt.Errorf("invalid ROUTINE_BODY: %s", args[7])
}
createReq.RoutineDefinition = args[8]
_, err = fmt.Sscan(args[9], &createReq.RoutineDependencies)
if err != nil {
return fmt.Errorf("invalid ROUTINE_DEPENDENCIES: %s", args[9])
}
_, err = fmt.Sscan(args[10], &createReq.ParameterStyle)
if err != nil {
return fmt.Errorf("invalid PARAMETER_STYLE: %s", args[10])
}
_, err = fmt.Sscan(args[11], &createReq.IsDeterministic)
if err != nil {
return fmt.Errorf("invalid IS_DETERMINISTIC: %s", args[11])
}
_, err = fmt.Sscan(args[12], &createReq.SqlDataAccess)
if err != nil {
return fmt.Errorf("invalid SQL_DATA_ACCESS: %s", args[12])
}
_, err = fmt.Sscan(args[13], &createReq.IsNullCall)
if err != nil {
return fmt.Errorf("invalid IS_NULL_CALL: %s", args[13])
}
_, err = fmt.Sscan(args[14], &createReq.SecurityType)
if err != nil {
return fmt.Errorf("invalid SECURITY_TYPE: %s", args[14])
}
createReq.SpecificName = args[15]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.Functions.Create(ctx, createReq)

View File

@ -53,6 +53,7 @@ func init() {
// TODO: map via StringToStringVar: custom_tags
// TODO: complex arg: disk_spec
createCmd.Flags().BoolVar(&createReq.EnableElasticDisk, "enable-elastic-disk", createReq.EnableElasticDisk, `Autoscaling Local Storage: when enabled, this instances in this pool will dynamically acquire additional disk space when its Spark workers are running low on disk space.`)
// TODO: complex arg: gcp_attributes
createCmd.Flags().IntVar(&createReq.IdleInstanceAutoterminationMinutes, "idle-instance-autotermination-minutes", createReq.IdleInstanceAutoterminationMinutes, `Automatically terminates the extra instances in the pool cache after they are inactive for this time in minutes if min_idle_instances requirement is already met.`)
// TODO: complex arg: instance_pool_fleet_attributes
createCmd.Flags().IntVar(&createReq.MaxCapacity, "max-capacity", createReq.MaxCapacity, `Maximum number of outstanding instances to keep in the pool, including both instances used by clusters and idle instances.`)
@ -179,6 +180,7 @@ func init() {
// TODO: map via StringToStringVar: custom_tags
// TODO: complex arg: disk_spec
editCmd.Flags().BoolVar(&editReq.EnableElasticDisk, "enable-elastic-disk", editReq.EnableElasticDisk, `Autoscaling Local Storage: when enabled, this instances in this pool will dynamically acquire additional disk space when its Spark workers are running low on disk space.`)
// TODO: complex arg: gcp_attributes
editCmd.Flags().IntVar(&editReq.IdleInstanceAutoterminationMinutes, "idle-instance-autotermination-minutes", editReq.IdleInstanceAutoterminationMinutes, `Automatically terminates the extra instances in the pool cache after they are inactive for this time in minutes if min_idle_instances requirement is already met.`)
// TODO: complex arg: instance_pool_fleet_attributes
editCmd.Flags().IntVar(&editReq.MaxCapacity, "max-capacity", editReq.MaxCapacity, `Maximum number of outstanding instances to keep in the pool, including both instances used by clusters and idle instances.`)

View File

@ -86,15 +86,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Label = args[0]
_, err = fmt.Sscan(args[1], &createReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &createReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.IpAccessLists.Create(ctx, createReq)
@ -303,20 +295,7 @@ var replaceCmd = &cobra.Command{
return err
}
} else {
replaceReq.Label = args[0]
_, err = fmt.Sscan(args[1], &replaceReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &replaceReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
_, err = fmt.Sscan(args[3], &replaceReq.Enabled)
if err != nil {
return fmt.Errorf("invalid ENABLED: %s", args[3])
}
replaceReq.IpAccessListId = args[4]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.IpAccessLists.Replace(ctx, replaceReq)
@ -377,20 +356,7 @@ var updateCmd = &cobra.Command{
return err
}
} else {
updateReq.Label = args[0]
_, err = fmt.Sscan(args[1], &updateReq.ListType)
if err != nil {
return fmt.Errorf("invalid LIST_TYPE: %s", args[1])
}
_, err = fmt.Sscan(args[2], &updateReq.IpAddresses)
if err != nil {
return fmt.Errorf("invalid IP_ADDRESSES: %s", args[2])
}
_, err = fmt.Sscan(args[3], &updateReq.Enabled)
if err != nil {
return fmt.Errorf("invalid ENABLED: %s", args[3])
}
updateReq.IpAccessListId = args[4]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.IpAccessLists.Update(ctx, updateReq)

View File

@ -912,14 +912,7 @@ var resetCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &resetReq.JobId)
if err != nil {
return fmt.Errorf("invalid JOB_ID: %s", args[0])
}
_, err = fmt.Sscan(args[1], &resetReq.NewSettings)
if err != nil {
return fmt.Errorf("invalid NEW_SETTINGS: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.Jobs.Reset(ctx, resetReq)

View File

@ -172,11 +172,7 @@ var installCmd = &cobra.Command{
return err
}
} else {
installReq.ClusterId = args[0]
_, err = fmt.Sscan(args[1], &installReq.Libraries)
if err != nil {
return fmt.Errorf("invalid LIBRARIES: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.Libraries.Install(ctx, installReq)
@ -222,11 +218,7 @@ var uninstallCmd = &cobra.Command{
return err
}
} else {
uninstallReq.ClusterId = args[0]
_, err = fmt.Sscan(args[1], &uninstallReq.Libraries)
if err != nil {
return fmt.Errorf("invalid LIBRARIES: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
err = w.Libraries.Uninstall(ctx, uninstallReq)

View File

@ -349,10 +349,7 @@ var createWebhookCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &createWebhookReq.Events)
if err != nil {
return fmt.Errorf("invalid EVENTS: %s", args[0])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.ModelRegistry.CreateWebhook(ctx, createWebhookReq)

View File

@ -120,11 +120,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.Name = args[0]
_, err = fmt.Sscan(args[1], &createReq.Config)
if err != nil {
return fmt.Errorf("invalid CONFIG: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
wait, err := w.ServingEndpoints.Create(ctx, createReq)
@ -474,11 +470,7 @@ var updateConfigCmd = &cobra.Command{
return err
}
} else {
_, err = fmt.Sscan(args[0], &updateConfigReq.ServedModels)
if err != nil {
return fmt.Errorf("invalid SERVED_MODELS: %s", args[0])
}
updateConfigReq.Name = args[1]
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
wait, err := w.ServingEndpoints.UpdateConfig(ctx, updateConfigReq)

View File

@ -72,11 +72,7 @@ var createCmd = &cobra.Command{
return err
}
} else {
createReq.FullNameArg = args[0]
_, err = fmt.Sscan(args[1], &createReq.Constraint)
if err != nil {
return fmt.Errorf("invalid CONSTRAINT: %s", args[1])
}
return fmt.Errorf("provide command input in JSON format by specifying --json option")
}
response, err := w.TableConstraints.Create(ctx, createReq)

View File

@ -119,9 +119,9 @@ var exportCmd = &cobra.Command{
If path does not exist, this call returns an error
RESOURCE_DOES_NOT_EXIST.
One can only export a directory in DBC format. If the exported data would
exceed size limit, this call returns MAX_NOTEBOOK_SIZE_EXCEEDED. Currently,
this API does not support exporting a library.`,
If the exported data would exceed size limit, this call returns
MAX_NOTEBOOK_SIZE_EXCEEDED. Currently, this API does not support exporting a
library.`,
Annotations: map[string]string{},
PreRunE: root.MustWorkspaceClient,
@ -286,7 +286,7 @@ func init() {
// TODO: short flags
listCmd.Flags().Var(&listJson, "json", `either inline JSON string or @path/to/file.json with request body`)
listCmd.Flags().IntVar(&listReq.NotebooksModifiedAfter, "notebooks-modified-after", listReq.NotebooksModifiedAfter, `<content needed>.`)
listCmd.Flags().IntVar(&listReq.NotebooksModifiedAfter, "notebooks-modified-after", listReq.NotebooksModifiedAfter, `UTC timestamp in milliseconds.`)
}
@ -353,7 +353,7 @@ var mkdirsCmd = &cobra.Command{
path, this call returns an error RESOURCE_ALREADY_EXISTS.
Note that if this operation fails it may have succeeded in creating some of
the necessary parrent directories.`,
the necessary parent directories.`,
Annotations: map[string]string{},
PreRunE: root.MustWorkspaceClient,

14
internal/alerts_test.go Normal file
View File

@ -0,0 +1,14 @@
package internal
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAccAlertsCreateErrWhenNoArguments(t *testing.T) {
t.Log(GetEnvOrSkipTest(t, "CLOUD_ENV"))
_, _, err := RequireErrorRun(t, "alerts", "create")
assert.Equal(t, "provide command input in JSON format by specifying --json option", err.Error())
}