Skip to content

Commit d0d5dd2

Browse files
authored
Merge pull request #111 from auth0/refactor/apps
Refactor apps [CLI-15]
2 parents a9a932f + 47b5645 commit d0d5dd2

File tree

5 files changed

+367
-270
lines changed

5 files changed

+367
-270
lines changed

internal/cli/apis.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ auth0 apis create --name myapi --identifier http://my-api
144144
if shouldPrompt(cmd, apiName) {
145145
input := prompt.TextInput(
146146
apiName, "Name:",
147-
"Name of the API. You can change the API name later in the API settings.",
147+
"Name of the API. You can change the name later in the API settings.",
148148
true)
149149

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

270270
return cmd
271271
}
@@ -300,15 +300,9 @@ auth0 apis delete --id id
300300
}
301301
}
302302

303-
err := ansi.Spinner("Deleting API", func() error {
303+
return ansi.Spinner("Deleting API", func() error {
304304
return cli.api.ResourceServer.Delete(flags.ID)
305305
})
306-
307-
if err != nil {
308-
return err
309-
}
310-
311-
return nil
312306
},
313307
}
314308

internal/cli/apps.go

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/auth0/auth0-cli/internal/ansi"
8+
"github.com/auth0/auth0-cli/internal/auth0"
9+
"github.com/auth0/auth0-cli/internal/prompt"
10+
"github.com/spf13/cobra"
11+
"gopkg.in/auth0.v5/management"
12+
)
13+
14+
const (
15+
appID = "id"
16+
appName = "name"
17+
appType = "type"
18+
appDescription = "description"
19+
)
20+
21+
func appsCmd(cli *cli) *cobra.Command {
22+
cmd := &cobra.Command{
23+
Use: "apps",
24+
Short: "Manage resources for applications",
25+
Aliases: []string{"clients"},
26+
}
27+
28+
cmd.SetUsageTemplate(resourceUsageTemplate())
29+
cmd.AddCommand(listAppsCmd(cli))
30+
cmd.AddCommand(createAppCmd(cli))
31+
cmd.AddCommand(updateAppCmd(cli))
32+
cmd.AddCommand(deleteAppCmd(cli))
33+
34+
return cmd
35+
}
36+
37+
func listAppsCmd(cli *cli) *cobra.Command {
38+
cmd := &cobra.Command{
39+
Use: "list",
40+
Short: "List your applications",
41+
Long: `auth0 apps list
42+
Lists your existing applications. To create one try:
43+
44+
auth0 apps create
45+
`,
46+
RunE: func(cmd *cobra.Command, args []string) error {
47+
var list *management.ClientList
48+
err := ansi.Spinner("Loading applications", func() error {
49+
var err error
50+
list, err = cli.api.Client.List()
51+
return err
52+
})
53+
54+
if err != nil {
55+
return err
56+
}
57+
58+
cli.renderer.ApplicationList(list.Clients)
59+
return nil
60+
},
61+
}
62+
63+
return cmd
64+
}
65+
66+
func deleteAppCmd(cli *cli) *cobra.Command {
67+
var flags struct {
68+
ID string
69+
}
70+
71+
cmd := &cobra.Command{
72+
Use: "delete",
73+
Short: "Delete an application",
74+
Long: `Delete an application:
75+
76+
auth0 apps delete --id id
77+
`,
78+
PreRun: func(cmd *cobra.Command, args []string) {
79+
prepareInteractivity(cmd)
80+
},
81+
RunE: func(cmd *cobra.Command, args []string) error {
82+
if shouldPrompt(cmd, appID) {
83+
input := prompt.TextInput(appID, "Id:", "Id of the application.", true)
84+
85+
if err := prompt.AskOne(input, &flags); err != nil {
86+
return err
87+
}
88+
}
89+
90+
if !cli.force && canPrompt(cmd) {
91+
if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed {
92+
return nil
93+
}
94+
}
95+
96+
return ansi.Spinner("Deleting application", func() error {
97+
return cli.api.Client.Delete(flags.ID)
98+
})
99+
},
100+
}
101+
102+
cmd.Flags().StringVarP(&flags.ID, appID, "i", "", "ID of the application.")
103+
mustRequireFlags(cmd, appID)
104+
105+
return cmd
106+
}
107+
108+
func createAppCmd(cli *cli) *cobra.Command {
109+
var flags struct {
110+
Name string
111+
Type string
112+
Description string
113+
Callbacks []string
114+
AuthMethod string
115+
}
116+
117+
cmd := &cobra.Command{
118+
Use: "create",
119+
Short: "Create a new application",
120+
Long: `Create a new application:
121+
122+
auth0 apps create --name myapp --type [native|spa|regular|m2m]
123+
`,
124+
PreRun: func(cmd *cobra.Command, args []string) {
125+
prepareInteractivity(cmd)
126+
},
127+
RunE: func(cmd *cobra.Command, args []string) error {
128+
if shouldPrompt(cmd, appName) {
129+
input := prompt.TextInput(
130+
appName, "Name:",
131+
"Name of the application. You can change the name later in the application settings.",
132+
true)
133+
134+
if err := prompt.AskOne(input, &flags); err != nil {
135+
return err
136+
}
137+
}
138+
139+
if shouldPrompt(cmd, appType) {
140+
input := prompt.SelectInput(
141+
appType,
142+
"Type:",
143+
"\n- Native: Mobile, desktop, CLI and smart device apps running natively."+
144+
"\n- Single Page Web Application: A JavaScript front-end app that uses an API."+
145+
"\n- Regular Web Application: Traditional web app using redirects."+
146+
"\n- Machine To Machine: CLIs, daemons or services running on your backend.",
147+
[]string{"Native", "Single Page Web Application", "Regular Web Application", "Machine to Machine"},
148+
true)
149+
150+
if err := prompt.AskOne(input, &flags); err != nil {
151+
return err
152+
}
153+
}
154+
155+
if shouldPrompt(cmd, appDescription) {
156+
input := prompt.TextInput(appDescription, "Description:", "Description of the application.", false)
157+
158+
if err := prompt.AskOne(input, &flags); err != nil {
159+
return err
160+
}
161+
}
162+
163+
a := &management.Client{
164+
Name: &flags.Name,
165+
Description: &flags.Description,
166+
AppType: auth0.String(apiTypeFor(flags.Type)),
167+
Callbacks: apiCallbacksFor(flags.Callbacks),
168+
TokenEndpointAuthMethod: apiAuthMethodFor(flags.AuthMethod),
169+
}
170+
171+
err := ansi.Spinner("Creating application", func() error {
172+
return cli.api.Client.Create(a)
173+
})
174+
175+
if err != nil {
176+
return err
177+
}
178+
179+
// note: c is populated with the rest of the client fields by the API during creation.
180+
revealClientSecret := auth0.StringValue(a.AppType) != "native" && auth0.StringValue(a.AppType) != "spa"
181+
cli.renderer.ApplicationCreate(a, revealClientSecret)
182+
183+
return nil
184+
},
185+
}
186+
187+
cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the application.")
188+
cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Type of application:\n"+
189+
"- native: mobile, desktop, CLI and smart device apps running natively.\n"+
190+
"- spa (single page application): a JavaScript front-end app that uses an API.\n"+
191+
"- regular: Traditional web app using redirects.\n"+
192+
"- m2m (machine to machine): CLIs, daemons or services running on your backend.")
193+
cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of the application. Max character count is 140.")
194+
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://.")
195+
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).")
196+
mustRequireFlags(cmd, appName, appType)
197+
198+
return cmd
199+
}
200+
201+
func updateAppCmd(cli *cli) *cobra.Command {
202+
var flags struct {
203+
ID string
204+
Name string
205+
Type string
206+
Description string
207+
Callbacks []string
208+
AuthMethod string
209+
}
210+
211+
cmd := &cobra.Command{
212+
Use: "update",
213+
Short: "Update a new application",
214+
Long: `Update a new application:
215+
216+
auth0 apps update --id id --name myapp --type [native|spa|regular|m2m]
217+
`,
218+
PreRun: func(cmd *cobra.Command, args []string) {
219+
prepareInteractivity(cmd)
220+
},
221+
RunE: func(cmd *cobra.Command, args []string) error {
222+
if shouldPrompt(cmd, appID) {
223+
input := prompt.TextInput(appID, "Id:", "Id of the application.", true)
224+
225+
if err := prompt.AskOne(input, &flags); err != nil {
226+
return err
227+
}
228+
}
229+
230+
if shouldPrompt(cmd, appName) {
231+
input := prompt.TextInput(appName, "Name:", "Name of the application", true)
232+
233+
if err := prompt.AskOne(input, &flags); err != nil {
234+
return err
235+
}
236+
}
237+
238+
if shouldPrompt(cmd, appType) {
239+
input := prompt.SelectInput(
240+
appType,
241+
"Type:",
242+
"\n- Native: Mobile, desktop, CLI and smart device apps running natively."+
243+
"\n- Single Page Web Application: A JavaScript front-end app that uses an API."+
244+
"\n- Regular Web Application: Traditional web app using redirects."+
245+
"\n- Machine To Machine: CLIs, daemons or services running on your backend.",
246+
[]string{"Native", "Single Page Web Application", "Regular Web Application", "Machine to Machine"},
247+
true)
248+
249+
if err := prompt.AskOne(input, &flags); err != nil {
250+
return err
251+
}
252+
}
253+
254+
if shouldPrompt(cmd, appDescription) {
255+
input := prompt.TextInput(appDescription, "Description:", "Description of the application.", false)
256+
257+
if err := prompt.AskOne(input, &flags); err != nil {
258+
return err
259+
}
260+
}
261+
262+
a := &management.Client{
263+
Name: &flags.Name,
264+
Description: &flags.Description,
265+
AppType: auth0.String(apiTypeFor(flags.Type)),
266+
Callbacks: apiCallbacksFor(flags.Callbacks),
267+
TokenEndpointAuthMethod: apiAuthMethodFor(flags.AuthMethod),
268+
}
269+
270+
err := ansi.Spinner("Updating application", func() error {
271+
return cli.api.Client.Update(flags.ID, a)
272+
})
273+
274+
if err != nil {
275+
return err
276+
}
277+
278+
// note: c is populated with the rest of the client fields by the API during creation.
279+
revealClientSecret := auth0.StringValue(a.AppType) != "native" && auth0.StringValue(a.AppType) != "spa"
280+
cli.renderer.ApplicationUpdate(a, revealClientSecret)
281+
282+
return nil
283+
},
284+
}
285+
286+
cmd.Flags().StringVarP(&flags.ID, appID, "i", "", "ID of the application.")
287+
cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the application.")
288+
cmd.Flags().StringVarP(&flags.Type, "type", "t", "", "Type of application:\n"+
289+
"- native: mobile, desktop, CLI and smart device apps running natively.\n"+
290+
"- spa (single page application): a JavaScript front-end app that uses an API.\n"+
291+
"- regular: Traditional web app using redirects.\n"+
292+
"- m2m (machine to machine): CLIs, daemons or services running on your backend.")
293+
cmd.Flags().StringVarP(&flags.Description, "description", "d", "", "Description of the application. Max character count is 140.")
294+
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://.")
295+
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).")
296+
mustRequireFlags(cmd, appID)
297+
298+
return cmd
299+
}
300+
301+
func apiTypeFor(v string) string {
302+
switch strings.ToLower(v) {
303+
case "native":
304+
return "native"
305+
case "spa", "single page web application":
306+
return "spa"
307+
case "regular", "regular web application":
308+
return "regular_web"
309+
case "m2m", "machine to machine":
310+
return "non_interactive"
311+
312+
default:
313+
return v
314+
}
315+
}
316+
317+
func apiCallbacksFor(s []string) []interface{} {
318+
res := make([]interface{}, len(s))
319+
for i, v := range s {
320+
res[i] = v
321+
}
322+
return res
323+
}
324+
325+
func apiAuthMethodFor(v string) *string {
326+
switch strings.ToLower(v) {
327+
case "none":
328+
return auth0.String("none")
329+
case "post":
330+
return auth0.String("client_secret_post")
331+
case "basic":
332+
return auth0.String("client_secret_basic")
333+
default:
334+
return nil
335+
}
336+
}
337+
338+
func callbacksFor(s []interface{}) []string {
339+
res := make([]string, len(s))
340+
for i, v := range s {
341+
res[i] = fmt.Sprintf("%s", v)
342+
}
343+
return res
344+
}

internal/cli/clients_test.go renamed to internal/cli/apps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestClientsListCmd(t *testing.T) {
4242
api: &auth0.API{Client: clientAPI},
4343
}
4444

45-
cmd := appsListCmd(cli)
45+
cmd := listAppsCmd(cli)
4646

4747
if err := cmd.Execute(); err != nil {
4848
t.Fatal(err)

0 commit comments

Comments
 (0)