Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor apps [CLI-15] #111

Merged
merged 8 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: we don't need apiName because it's optional for creation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's optional for update (you can update any property). For create it's required.


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
Comment on lines -303 to -311
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#winning

},
}

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
}
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