Skip to content

Commit

Permalink
Merge pull request #111 from auth0/refactor/apps
Browse files Browse the repository at this point in the history
Refactor apps [CLI-15]
  • Loading branch information
Widcket authored Feb 26, 2021
2 parents a9a932f + 47b5645 commit d0d5dd2
Show file tree
Hide file tree
Showing 5 changed files with 367 additions and 270 deletions.
12 changes: 3 additions & 9 deletions internal/cli/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ auth0 apis create --name myapi --identifier http://my-api
if shouldPrompt(cmd, apiName) {
input := prompt.TextInput(
apiName, "Name:",
"Name of the API. You can change the API name later in the API settings.",
"Name of the API. You can change the name later in the API settings.",
true)

if err := prompt.AskOne(input, &flags); err != nil {
Expand Down Expand Up @@ -265,7 +265,7 @@ auth0 apis update --id id --name myapi
cmd.Flags().StringVarP(&flags.ID, apiID, "i", "", "ID of the API.")
cmd.Flags().StringVarP(&flags.Name, apiName, "n", "", "Name of the API.")
cmd.Flags().StringVarP(&flags.Scopes, apiScopes, "s", "", "Space-separated list of scopes.")
mustRequireFlags(cmd, apiID, apiName)
mustRequireFlags(cmd, apiID)

return cmd
}
Expand Down Expand Up @@ -300,15 +300,9 @@ auth0 apis delete --id id
}
}

err := ansi.Spinner("Deleting API", func() error {
return ansi.Spinner("Deleting API", func() error {
return cli.api.ResourceServer.Delete(flags.ID)
})

if err != nil {
return err
}

return nil
},
}

Expand Down
344 changes: 344 additions & 0 deletions internal/cli/apps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
package cli

import (
"fmt"
"strings"

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/auth0"
"github.com/auth0/auth0-cli/internal/prompt"
"github.com/spf13/cobra"
"gopkg.in/auth0.v5/management"
)

const (
appID = "id"
appName = "name"
appType = "type"
appDescription = "description"
)

func appsCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "apps",
Short: "Manage resources for applications",
Aliases: []string{"clients"},
}

cmd.SetUsageTemplate(resourceUsageTemplate())
cmd.AddCommand(listAppsCmd(cli))
cmd.AddCommand(createAppCmd(cli))
cmd.AddCommand(updateAppCmd(cli))
cmd.AddCommand(deleteAppCmd(cli))

return cmd
}

func listAppsCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List your applications",
Long: `auth0 apps list
Lists your existing applications. To create one try:
auth0 apps create
`,
RunE: func(cmd *cobra.Command, args []string) error {
var list *management.ClientList
err := ansi.Spinner("Loading applications", func() error {
var err error
list, err = cli.api.Client.List()
return err
})

if err != nil {
return err
}

cli.renderer.ApplicationList(list.Clients)
return nil
},
}

return cmd
}

func deleteAppCmd(cli *cli) *cobra.Command {
var flags struct {
ID string
}

cmd := &cobra.Command{
Use: "delete",
Short: "Delete an application",
Long: `Delete an application:
auth0 apps delete --id id
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
if shouldPrompt(cmd, appID) {
input := prompt.TextInput(appID, "Id:", "Id of the application.", true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if !cli.force && canPrompt(cmd) {
if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed {
return nil
}
}

return ansi.Spinner("Deleting application", func() error {
return cli.api.Client.Delete(flags.ID)
})
},
}

cmd.Flags().StringVarP(&flags.ID, appID, "i", "", "ID of the application.")
mustRequireFlags(cmd, appID)

return cmd
}

func createAppCmd(cli *cli) *cobra.Command {
var flags struct {
Name string
Type string
Description string
Callbacks []string
AuthMethod string
}

cmd := &cobra.Command{
Use: "create",
Short: "Create a new application",
Long: `Create a new application:
auth0 apps create --name myapp --type [native|spa|regular|m2m]
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
if shouldPrompt(cmd, appName) {
input := prompt.TextInput(
appName, "Name:",
"Name of the application. You can change the name later in the application settings.",
true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if shouldPrompt(cmd, appType) {
input := prompt.SelectInput(
appType,
"Type:",
"\n- Native: Mobile, desktop, CLI and smart device apps running natively."+
"\n- Single Page Web Application: A JavaScript front-end app that uses an API."+
"\n- Regular Web Application: Traditional web app using redirects."+
"\n- Machine To Machine: CLIs, daemons or services running on your backend.",
[]string{"Native", "Single Page Web Application", "Regular Web Application", "Machine to Machine"},
true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if shouldPrompt(cmd, appDescription) {
input := prompt.TextInput(appDescription, "Description:", "Description of the application.", false)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

a := &management.Client{
Name: &flags.Name,
Description: &flags.Description,
AppType: auth0.String(apiTypeFor(flags.Type)),
Callbacks: apiCallbacksFor(flags.Callbacks),
TokenEndpointAuthMethod: apiAuthMethodFor(flags.AuthMethod),
}

err := ansi.Spinner("Creating application", func() error {
return cli.api.Client.Create(a)
})

if err != nil {
return err
}

// note: c 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.ApplicationCreate(a, revealClientSecret)

return nil
},
}

cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the application.")
cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Type of application:\n"+
"- native: mobile, desktop, CLI and smart device apps running natively.\n"+
"- spa (single page application): a JavaScript front-end app that uses an API.\n"+
"- regular: Traditional web app using redirects.\n"+
"- m2m (machine to machine): CLIs, daemons or services running on your backend.")
cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of the application. Max character count is 140.")
cmd.Flags().StringSliceVarP(&flags.Callbacks, "callbacks", "c", nil, "After the user authenticates we will only call back to any of these URLs. You can specify multiple valid URLs by comma-separating them (typically to handle different environments like QA or testing). Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native apps, all callbacks should use protocol https://.")
cmd.Flags().StringVar(&flags.AuthMethod, "auth-method", "", "Defines the requested authentication method for the token endpoint. Possible values are 'None' (public application without a client secret), 'Post' (application uses HTTP POST parameters) or 'Basic' (application uses HTTP Basic).")
mustRequireFlags(cmd, appName, appType)

return cmd
}

func updateAppCmd(cli *cli) *cobra.Command {
var flags struct {
ID string
Name string
Type string
Description string
Callbacks []string
AuthMethod string
}

cmd := &cobra.Command{
Use: "update",
Short: "Update a new application",
Long: `Update a new application:
auth0 apps update --id id --name myapp --type [native|spa|regular|m2m]
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
if shouldPrompt(cmd, appID) {
input := prompt.TextInput(appID, "Id:", "Id of the application.", true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if shouldPrompt(cmd, appName) {
input := prompt.TextInput(appName, "Name:", "Name of the application", true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if shouldPrompt(cmd, appType) {
input := prompt.SelectInput(
appType,
"Type:",
"\n- Native: Mobile, desktop, CLI and smart device apps running natively."+
"\n- Single Page Web Application: A JavaScript front-end app that uses an API."+
"\n- Regular Web Application: Traditional web app using redirects."+
"\n- Machine To Machine: CLIs, daemons or services running on your backend.",
[]string{"Native", "Single Page Web Application", "Regular Web Application", "Machine to Machine"},
true)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

if shouldPrompt(cmd, appDescription) {
input := prompt.TextInput(appDescription, "Description:", "Description of the application.", false)

if err := prompt.AskOne(input, &flags); err != nil {
return err
}
}

a := &management.Client{
Name: &flags.Name,
Description: &flags.Description,
AppType: auth0.String(apiTypeFor(flags.Type)),
Callbacks: apiCallbacksFor(flags.Callbacks),
TokenEndpointAuthMethod: apiAuthMethodFor(flags.AuthMethod),
}

err := ansi.Spinner("Updating application", func() error {
return cli.api.Client.Update(flags.ID, a)
})

if err != nil {
return err
}

// note: c 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)

return nil
},
}

cmd.Flags().StringVarP(&flags.ID, appID, "i", "", "ID of the application.")
cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the application.")
cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Type of application:\n"+
"- native: mobile, desktop, CLI and smart device apps running natively.\n"+
"- spa (single page application): a JavaScript front-end app that uses an API.\n"+
"- regular: Traditional web app using redirects.\n"+
"- m2m (machine to machine): CLIs, daemons or services running on your backend.")
cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of the application. Max character count is 140.")
cmd.Flags().StringSliceVarP(&flags.Callbacks, "callbacks", "c", nil, "After the user authenticates we will only call back to any of these URLs. You can specify multiple valid URLs by comma-separating them (typically to handle different environments like QA or testing). Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native apps, all callbacks should use protocol https://.")
cmd.Flags().StringVar(&flags.AuthMethod, "auth-method", "", "Defines the requested authentication method for the token endpoint. Possible values are 'None' (public application without a client secret), 'Post' (application uses HTTP POST parameters) or 'Basic' (application uses HTTP Basic).")
mustRequireFlags(cmd, appID)

return cmd
}

func apiTypeFor(v string) string {
switch strings.ToLower(v) {
case "native":
return "native"
case "spa", "single page web application":
return "spa"
case "regular", "regular web application":
return "regular_web"
case "m2m", "machine to machine":
return "non_interactive"

default:
return v
}
}

func apiCallbacksFor(s []string) []interface{} {
res := make([]interface{}, len(s))
for i, v := range s {
res[i] = v
}
return res
}

func apiAuthMethodFor(v string) *string {
switch strings.ToLower(v) {
case "none":
return auth0.String("none")
case "post":
return auth0.String("client_secret_post")
case "basic":
return auth0.String("client_secret_basic")
default:
return nil
}
}

func callbacksFor(s []interface{}) []string {
res := make([]string, len(s))
for i, v := range s {
res[i] = fmt.Sprintf("%s", v)
}
return res
}
2 changes: 1 addition & 1 deletion internal/cli/clients_test.go → internal/cli/apps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestClientsListCmd(t *testing.T) {
api: &auth0.API{Client: clientAPI},
}

cmd := appsListCmd(cli)
cmd := listAppsCmd(cli)

if err := cmd.Execute(); err != nil {
t.Fatal(err)
Expand Down
Loading

0 comments on commit d0d5dd2

Please sign in to comment.