diff --git a/internal/cli/actions.go b/internal/cli/actions.go index a0ee593a6..a03535ed7 100644 --- a/internal/cli/actions.go +++ b/internal/cli/actions.go @@ -127,7 +127,7 @@ auth0 actions test --file payload.json`, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Action Id to test.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -201,7 +201,7 @@ auth0 actions deploy --version version-id`, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Action Id to deploy.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -298,7 +298,7 @@ auth0 actions download --version `, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Action Id to download.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -391,7 +391,7 @@ auth0 actions versions `, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Action Id to show versions.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -567,9 +567,12 @@ func updateActionCmd(cli *cli) *cobra.Command { $ auth0 actions update --file action.js --dependency lodash@4.17.19 `, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Id of the action.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -651,9 +654,12 @@ func deleteActionCmd(cli *cli) *cobra.Command { Long: `Delete an action: $ auth0 actions delete `, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Id:", "Id of the action.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -704,7 +710,7 @@ auth0 actions flows show `, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionTrigger) { + if canPrompt(cmd) { input := prompt.SelectInput( actionTrigger, "Trigger:", @@ -764,7 +770,7 @@ auth0 actions flows update --file bindings.json`, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionTrigger) { + if canPrompt(cmd) { input := prompt.SelectInput( actionTrigger, "Trigger:", @@ -845,7 +851,7 @@ auth0 actions bind --trigger post-login`, }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, actionID) { + if canPrompt(cmd) { input := prompt.TextInput(actionID, "Action Id:", "Action Id to bind.", false) if err := prompt.AskOne(input, &inputs); err != nil { diff --git a/internal/cli/apps.go b/internal/cli/apps.go index f196d0c83..ec3d40c79 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -17,6 +17,7 @@ const ( appName = "name" appType = "type" appDescription = "description" + appCallback = "callbacks" ) func appsCmd(cli *cli) *cobra.Command { @@ -83,7 +84,7 @@ auth0 apps show }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, appID) { + if canPrompt(cmd) { input := prompt.TextInput(appID, "Id:", "Id of the application.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -135,7 +136,7 @@ auth0 apps delete }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, appID) { + if canPrompt(cmd) { input := prompt.TextInput(appID, "Id:", "Id of the application.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -235,7 +236,7 @@ auth0 apps create --name myapp --type [native|spa|regular|m2m] OIDCConformant: &oidcConformant, } - if (len(flags.Grants) == 0) { + if len(flags.Grants) == 0 { a.GrantTypes = apiDefaultGrantsFor(flags.Type) } else { a.GrantTypes = apiGrantsFor(flags.Grants) @@ -282,6 +283,7 @@ func updateAppCmd(cli *cli) *cobra.Command { Type string Description string Callbacks []string + CallbacksString string AllowedOrigins []string AllowedWebOrigins []string AllowedLogoutURLs []string @@ -302,7 +304,7 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, appID) { + if canPrompt(cmd) { input := prompt.TextInput(appID, "Id:", "Id of the application.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -315,7 +317,7 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] inputs.ID = args[0] } - if shouldPrompt(cmd, appName) { + if shouldPromptWhenFlagless(cmd, appName) { input := prompt.TextInput(appName, "Name:", "Name of the application", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -323,7 +325,7 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] } } - if shouldPrompt(cmd, appType) { + if shouldPromptWhenFlagless(cmd, appType) { input := prompt.SelectInput( appType, "Type:", @@ -339,7 +341,7 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] } } - if shouldPrompt(cmd, appDescription) { + if shouldPromptWhenFlagless(cmd, appDescription) { input := prompt.TextInput(appDescription, "Description:", "Description of the application.", false) if err := prompt.AskOne(input, &inputs); err != nil { @@ -347,27 +349,88 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] } } - a := &management.Client{ - Name: &inputs.Name, - Description: &inputs.Description, - AppType: auth0.String(apiTypeFor(inputs.Type)), - Callbacks: stringToInterfaceSlice(inputs.Callbacks), - AllowedOrigins: stringToInterfaceSlice(inputs.AllowedOrigins), - WebOrigins: stringToInterfaceSlice(inputs.AllowedWebOrigins), - AllowedLogoutURLs: stringToInterfaceSlice(inputs.AllowedLogoutURLs), - TokenEndpointAuthMethod: apiAuthMethodFor(inputs.AuthMethod), - GrantTypes: apiGrantsFor(inputs.Grants), + if shouldPromptWhenFlagless(cmd, "CallbacksString") { + input := prompt.TextInput("CallbacksString", "Callback URLs:", "Callback URLs of the application, comma-separated.", false) + + if err := prompt.AskOne(input, &inputs); err != nil { + return fmt.Errorf("An unexpected error occurred: %w", err) + } } - err := ansi.Spinner("Updating application", func() error { + a := &management.Client{} + + updateErr := ansi.Spinner("Updating application", func() error { + current, readErr := cli.api.Client.Read(inputs.ID) + + if readErr != nil { + return fmt.Errorf("Unable to load application. The id %v specified doesn't exist", inputs.ID) + } + + if len(inputs.Name) == 0 { + a.Name = current.Name + } else { + a.Name = &inputs.Name + } + + if len(inputs.Description) == 0 { + a.Description = current.Description + } else { + a.Description = &inputs.Description + } + + if len(inputs.Type) == 0 { + a.AppType = current.AppType + } else { + a.AppType = auth0.String(apiTypeFor(inputs.Type)) + } + + if len(inputs.Callbacks) == 0 { + if len(inputs.CallbacksString) == 0 { + a.Callbacks = current.Callbacks + } else { + a.Callbacks = stringToInterfaceSlice(commaSeparatedStringToSlice(inputs.CallbacksString)) + } + } else { + a.Callbacks = stringToInterfaceSlice(inputs.Callbacks) + } + + if len(inputs.AllowedOrigins) == 0 { + a.AllowedOrigins = current.AllowedOrigins + } else { + a.AllowedOrigins = stringToInterfaceSlice(inputs.AllowedOrigins) + } + + if len(inputs.AllowedWebOrigins) == 0 { + a.WebOrigins = current.WebOrigins + } else { + a.WebOrigins = stringToInterfaceSlice(inputs.AllowedWebOrigins) + } + + if len(inputs.AllowedLogoutURLs) == 0 { + a.AllowedLogoutURLs = current.AllowedLogoutURLs + } else { + a.AllowedLogoutURLs = stringToInterfaceSlice(inputs.AllowedLogoutURLs) + } + + if len(inputs.AuthMethod) == 0 { + a.TokenEndpointAuthMethod = current.TokenEndpointAuthMethod + } else { + a.TokenEndpointAuthMethod = apiAuthMethodFor(inputs.AuthMethod) + } + + if len(inputs.Grants) == 0 { + a.GrantTypes = current.GrantTypes + } else { + a.GrantTypes = apiGrantsFor(inputs.Grants) + } + return cli.api.Client.Update(inputs.ID, a) }) - if err != nil { - return fmt.Errorf("Unable to update application %v: %v", inputs.ID, err) + if updateErr != nil { + return fmt.Errorf("Unable to update application %v: %v", inputs.ID, updateErr) } - // note: a is populated with the rest of the client fields by the API during creation. revealClientSecret := auth0.StringValue(a.AppType) != "native" && auth0.StringValue(a.AppType) != "spa" cli.renderer.ApplicationUpdate(a, revealClientSecret) @@ -455,16 +518,16 @@ func apiGrantsFor(s []string) []interface{} { func apiDefaultGrantsFor(t string) []interface{} { switch apiTypeFor(strings.ToLower(t)) { - case "native": - return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token"}) - case "spa": - return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token"}) - case "regular_web": - return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token", "client_credentials"}) - case "non_interactive": - return stringToInterfaceSlice([]string{"client_credentials"}) - default: - return nil + case "native": + return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token"}) + case "spa": + return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token"}) + case "regular_web": + return stringToInterfaceSlice([]string{"implicit", "authorization_code", "refresh_token", "client_credentials"}) + case "non_interactive": + return stringToInterfaceSlice([]string{"client_credentials"}) + default: + return nil } } @@ -476,6 +539,10 @@ func urlsFor(s []interface{}) []string { return res } +func commaSeparatedStringToSlice(s string) []string { + return strings.Split(strings.Join(strings.Fields(s), ""), ",") +} + func stringToInterfaceSlice(s []string) []interface{} { var result []interface{} = make([]interface{}, len(s)) for i, d := range s { diff --git a/internal/cli/cli.go b/internal/cli/cli.go index d92b19468..4c8e2dac6 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -307,6 +307,18 @@ func shouldPrompt(cmd *cobra.Command, flag string) bool { return canPrompt(cmd) && !cmd.Flags().Changed(flag) } +func shouldPromptWhenFlagless(cmd *cobra.Command, flag string) bool { + isSet := false + + cmd.Flags().VisitAll(func(f *pflag.Flag) { + if (f.Changed) { + isSet = true + } + }) + + return canPrompt(cmd) && !isSet +} + func prepareInteractivity(cmd *cobra.Command) { if canPrompt(cmd) { cmd.Flags().VisitAll(func(flag *pflag.Flag) {