diff --git a/internal/cli/actions.go b/internal/cli/actions.go index a0ee593a6..bde344eed 100644 --- a/internal/cli/actions.go +++ b/internal/cli/actions.go @@ -127,14 +127,14 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] @@ -201,14 +201,14 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] @@ -298,14 +298,14 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] @@ -391,14 +391,14 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] @@ -567,22 +567,25 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] } - if shouldPrompt(cmd, actionFile) && shouldPrompt(cmd, actionScript) { + if shouldPromptWhenFlagless(cmd, actionFile) && shouldPrompt(cmd, actionScript) { input := prompt.TextInput(actionFile, "Action File:", "File containing the action source code.", false) if err := prompt.AskOne(input, &inputs); err != nil { @@ -633,7 +636,7 @@ $ auth0 actions update --file action.js --dependency lodash@4.17.19 cmd.Flags().BoolVarP(&inputs.CreateVersion, actionVersion, "v", false, "Create an explicit action version from the source code instead of a draft.") if err := cmd.MarkFlagFilename(actionFile); err != nil { - panic(err) + fmt.Println(fmt.Errorf("An unexpected error occurred: %w", err)) } return cmd @@ -651,16 +654,19 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] @@ -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:", @@ -716,7 +722,7 @@ auth0 actions flows show `, return err } } else { - return errors.New("missing action trigger") + return errors.New("Please provide an action trigger") } } else { inputs.Trigger = args[0] @@ -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:", @@ -776,13 +782,13 @@ auth0 actions flows update --file bindings.json`, return err } } else { - return errors.New("missing action trigger") + return errors.New("Please provide an action trigger") } } else { inputs.Trigger = args[0] } - if shouldPrompt(cmd, actionFile) { + if shouldPromptWhenFlagless(cmd, actionFile) { input := prompt.TextInput(actionFile, "File:", "File containing the bindings.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -845,14 +851,14 @@ 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 { return err } } else { - return errors.New("missing action id") + return errors.New("Please provide an action Id") } } else { inputs.ID = args[0] diff --git a/internal/cli/apis.go b/internal/cli/apis.go index e8707252c..1e6b50d27 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -96,14 +96,14 @@ auth0 apis show }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, apiID) { + if canPrompt(cmd) { input := prompt.TextInput(apiID, "Id:", "Id of the API.", true) if err := prompt.AskOne(input, &inputs); err != nil { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please include an API id") + return errors.New("Please include an API Id") } } else { inputs.ID = args[0] @@ -118,7 +118,7 @@ auth0 apis show }) if err != nil { - return fmt.Errorf("Unable to get an API with id %s: %w", inputs.ID, err) + return fmt.Errorf("Unable to get an API with Id %s: %w", inputs.ID, err) } cli.renderer.ApiShow(api) @@ -183,7 +183,7 @@ auth0 apis create --name myapi --identifier http://my-api } if flags.Scopes != "" { - api.Scopes = getScopes(flags.Scopes) + api.Scopes = apiScopesFor(flags.Scopes) } err := ansi.Spinner("Creating API", func() error { @@ -227,20 +227,20 @@ auth0 apis update --name myapi }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, apiID) { + if canPrompt(cmd) { input := prompt.TextInput(apiID, "Id:", "Id of the API.", true) if err := prompt.AskOne(input, &inputs); err != nil { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please include an API id") + return errors.New("Please include an API Id") } } else { inputs.ID = args[0] } - if shouldPrompt(cmd, apiName) { + if shouldPromptWhenFlagless(cmd, apiName) { input := prompt.TextInput(apiName, "Name:", "Name of the API.", true) if err := prompt.AskOne(input, &inputs); err != nil { @@ -248,7 +248,7 @@ auth0 apis update --name myapi } } - if shouldPrompt(cmd, apiScopes) { + if shouldPromptWhenFlagless(cmd, apiScopes) { input := prompt.TextInput(apiScopes, "Scopes:", "Space-separated list of scopes.", false) if err := prompt.AskOne(input, &inputs); err != nil { @@ -256,18 +256,32 @@ auth0 apis update --name myapi } } - api := &management.ResourceServer{Name: &inputs.Name} - - if inputs.Scopes != "" { - api.Scopes = getScopes(inputs.Scopes) - } + api := &management.ResourceServer{} err := ansi.Spinner("Updating API", func() error { + current, err := cli.api.ResourceServer.Read(inputs.ID) + + if err != nil { + return fmt.Errorf("Unable to load API. The Id %v specified doesn't exist", inputs.ID) + } + + if len(inputs.Name) == 0 { + api.Name = current.Name + } else { + api.Name = &inputs.Name + } + + if len(inputs.Scopes) == 0 { + api.Scopes = current.Scopes + } else { + api.Scopes = apiScopesFor(inputs.Scopes) + } + return cli.api.ResourceServer.Update(inputs.ID, api) }) if err != nil { - return fmt.Errorf("An unexpected error occurred while trying to update an API with id %s: %w", inputs.ID, err) + return fmt.Errorf("An unexpected error occurred while trying to update an API with Id %s: %w", inputs.ID, err) } cli.renderer.ApiUpdate(api) @@ -299,14 +313,14 @@ auth0 apis delete }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, apiID) { + if canPrompt(cmd) { input := prompt.TextInput(apiID, "Id:", "Id of the API.", true) if err := prompt.AskOne(input, &inputs); err != nil { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please include an API id") + return errors.New("Please include an API Id") } } else { inputs.ID = args[0] @@ -321,7 +335,7 @@ auth0 apis delete return ansi.Spinner("Deleting API", func() error { err := cli.api.ResourceServer.Delete(inputs.ID) if err != nil { - return fmt.Errorf("An unexpected error occurred while attempting to delete an API with id %s: %w", inputs.ID, err) + return fmt.Errorf("An unexpected error occurred while attempting to delete an API with Id %s: %w", inputs.ID, err) } return nil }) @@ -349,14 +363,14 @@ auth0 apis scopes list }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - if shouldPrompt(cmd, apiID) { + if canPrompt(cmd) { input := prompt.TextInput(apiID, "Id:", "Id of the API.", true) if err := prompt.AskOne(input, &inputs); err != nil { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please include an API id") + return errors.New("Please include an API Id") } } else { inputs.ID = args[0] @@ -371,7 +385,7 @@ auth0 apis scopes list }) if err != nil { - return fmt.Errorf("An unexpected error occurred while getting scopes for an API with id %s: %w", inputs.ID, err) + return fmt.Errorf("An unexpected error occurred while getting scopes for an API with Id %s: %w", inputs.ID, err) } cli.renderer.ScopesList(api.GetName(), api.Scopes) @@ -382,7 +396,7 @@ auth0 apis scopes list return cmd } -func getScopes(scopes string) []*management.ResourceServerScope { +func apiScopesFor(scopes string) []*management.ResourceServerScope { list := strings.Fields(scopes) models := []*management.ResourceServerScope{} diff --git a/internal/cli/apps.go b/internal/cli/apps.go index f196d0c83..dc6d4e82d 100644 --- a/internal/cli/apps.go +++ b/internal/cli/apps.go @@ -83,14 +83,14 @@ 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 { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please provide an application id") + return errors.New("Please provide an application Id") } } else { inputs.ID = args[0] @@ -105,7 +105,7 @@ auth0 apps show }) if err != nil { - return fmt.Errorf("Unable to load application. The id %v specified doesn't exist", inputs.ID) + return fmt.Errorf("Unable to load application. The Id %v specified doesn't exist", inputs.ID) } revealClientSecret := auth0.StringValue(a.AppType) != "native" && auth0.StringValue(a.AppType) != "spa" @@ -135,14 +135,14 @@ 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 { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please provide an application id") + return errors.New("Please provide an application Id") } } else { inputs.ID = args[0] @@ -235,7 +235,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 +282,7 @@ func updateAppCmd(cli *cli) *cobra.Command { Type string Description string Callbacks []string + CallbacksString string AllowedOrigins []string AllowedWebOrigins []string AllowedLogoutURLs []string @@ -302,20 +303,20 @@ 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 { return fmt.Errorf("An unexpected error occurred: %w", err) } } else { - return errors.New("Please provide an application id") + return errors.New("Please provide an application Id") } } else { 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 +324,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 +340,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,19 +348,81 @@ 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) + } } + a := &management.Client{} + err := ansi.Spinner("Updating application", func() error { + current, err := cli.api.Client.Read(inputs.ID) + + if err != 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) }) @@ -367,7 +430,6 @@ auth0 apps update --name myapp --type [native|spa|regular|m2m] return fmt.Errorf("Unable to update application %v: %v", inputs.ID, err) } - // 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 +517,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 +538,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..d61805d7c 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.LocalFlags().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) {