Skip to content

Commit

Permalink
apps picker: allow configurability of default app (#205)
Browse files Browse the repository at this point in the history
* apps picker: allow configurability of default app

Closes #190

* Show hint with the client id

* Update internal/cli/apps.go

Co-authored-by: Rita Zerrizuela <[email protected]>

Co-authored-by: Rita Zerrizuela <[email protected]>
  • Loading branch information
cyx and Widcket authored Mar 29, 2021
1 parent 2fcc616 commit 734443a
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 17 deletions.
104 changes: 93 additions & 11 deletions internal/cli/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ var (
Help: "Name of the application.",
IsRequired: true,
}
appNone = Flag{
Name: "None",
LongForm: "none",
ShortForm: "n",
Help: "Specify none of your apps",
}

appType = Flag{
Name: "Type",
LongForm: "type",
Expand Down Expand Up @@ -65,11 +72,11 @@ var (
AlwaysPrompt: true,
}
appOrigins = Flag{
Name: "Allowed Origin URLs",
LongForm: "origins",
ShortForm: "o",
Help: "Comma-separated list of URLs allowed to make requests from JavaScript to Auth0 API (typically used with CORS). By default, all your callback URLs will be allowed. This field allows you to enter other origins if necessary. You can also use wildcards at the subdomain level (e.g., https://*.contoso.com). Query strings and hash information are not taken into account when validating these URLs.",
IsRequired: false,
Name: "Allowed Origin URLs",
LongForm: "origins",
ShortForm: "o",
Help: "Comma-separated list of URLs allowed to make requests from JavaScript to Auth0 API (typically used with CORS). By default, all your callback URLs will be allowed. This field allows you to enter other origins if necessary. You can also use wildcards at the subdomain level (e.g., https://*.contoso.com). Query strings and hash information are not taken into account when validating these URLs.",
IsRequired: false,
AlwaysPrompt: true,
}
appWebOrigins = Flag{
Expand Down Expand Up @@ -117,10 +124,60 @@ func appsCmd(cli *cli) *cobra.Command {
cmd.AddCommand(showAppCmd(cli))
cmd.AddCommand(updateAppCmd(cli))
cmd.AddCommand(deleteAppCmd(cli))
cmd.AddCommand(useAppCmd(cli))

return cmd
}

func useAppCmd(cli *cli) *cobra.Command {
var inputs struct {
ID string
None bool
}

cmd := &cobra.Command{
Use: "use",
Short: "Choose a default application",
Long: `auth0 apps use <client-id>
Specify your preferred application for interaction with the Auth0 CLI
`,
PreRun: func(cmd *cobra.Command, args []string) {
prepareInteractivity(cmd)
},

RunE: func(cmd *cobra.Command, args []string) error {
if inputs.None {
inputs.ID = ""
} else {
if len(args) == 0 {
err := appID.Pick(cmd, &inputs.ID, cli.appPickerOptions)
if err != nil {
return err
}
} else {
inputs.ID = args[0]
}
}

if err := cli.setDefaultAppID(inputs.ID); err != nil {
return err
}

if inputs.ID == "" {
cli.renderer.Infof("Successfully removed the default application")
} else {
cli.renderer.Infof("Successfully set the default application to %s", ansi.Faint(inputs.ID))
cli.renderer.Infof("%s You might wanna try 'auth0 quickstarts download %s'", ansi.Faint("Hint:"), inputs.ID)
}

return nil
},
}

appNone.RegisterBool(cmd, &inputs.None, false)
return cmd
}

func listAppsCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Expand Down Expand Up @@ -352,6 +409,10 @@ auth0 apps create --name myapp --type [native|spa|regular|m2m]
return fmt.Errorf("Unable to create application: %w", err)
}

if err := cli.setDefaultAppID(a.GetClientID()); err != nil {
return err
}

// Render result
// 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"
Expand Down Expand Up @@ -713,19 +774,40 @@ func (c *cli) appPickerOptions() (pickerOptions, error) {
return nil, err
}

// NOTE: because client names are not unique, we'll just number these
// labels.
var opts pickerOptions
tenant, err := c.getTenant()
if err != nil {
return nil, err
}

// NOTE(cyx): To keep the contract for this simple, we'll rely on the
// implicit knowledge that the default value for the picker is the
// first option. With that in mind, we'll use the state in
// tenant.DefaultAppID to determine which should be chosen as the
// default.
var (
priorityOpts, opts pickerOptions
)
for _, c := range list.Clients {
// empty type means the default client that we shouldn't display.
if c.GetAppType() == "" {
continue
}

value := c.GetClientID()
label := fmt.Sprintf("%s %s", c.GetName(), ansi.Faint("("+value+")"))
opt := pickerOption{value: value, label: label}

opts = append(opts, pickerOption{value: value, label: label})
// check if this is currently the default application.
if tenant.DefaultAppID == c.GetClientID() {
priorityOpts = append(priorityOpts, opt)
} else {
opts = append(opts, opt)
}
}

if len(opts) == 0 {
if len(opts)+len(priorityOpts) == 0 {
return nil, errors.New("There are currently no applications.")
}

return opts, nil
return append(priorityOpts, opts...), nil
}
28 changes: 22 additions & 6 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ type config struct {
// tenant is the cli's concept of an auth0 tenant. The fields are tailor fit
// specifically for interacting with the management API.
type tenant struct {
Name string `json:"name"`
Domain string `json:"domain"`
AccessToken string `json:"access_token,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
Apps map[string]app `json:"apps,omitempty"`
Name string `json:"name"`
Domain string `json:"domain"`
AccessToken string `json:"access_token,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
Apps map[string]app `json:"apps,omitempty"`
DefaultAppID string `json:"default_app_id,omitempty"`
}

type app struct {
Expand Down Expand Up @@ -296,9 +297,24 @@ func (c *cli) isFirstCommandRun(clientID string, command string) (bool, error) {
return true, nil
}

func (c *cli) setFirstCommandRun(clientID string, command string) error {
func (c *cli) setDefaultAppID(id string) error {
tenant, err := c.getTenant()
if err != nil {
return err
}

tenant.DefaultAppID = id

c.config.Tenants[tenant.Name] = tenant
if err := c.persistConfig(); err != nil {
return fmt.Errorf("Unexpected error persisting config: %w", err)
}

return nil
}

func (c *cli) setFirstCommandRun(clientID string, command string) error {
tenant, err := c.getTenant()
if err != nil {
return err
}
Expand Down

0 comments on commit 734443a

Please sign in to comment.