Skip to content

Commit

Permalink
Refactor and improve users import cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiught committed Apr 13, 2023
1 parent 59a0cad commit 538b6bf
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 93 deletions.
13 changes: 10 additions & 3 deletions docs/auth0_users_import.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ auth0 users import [flags]
```
auth0 users import
auth0 users import --connection "Username-Password-Authentication"
auth0 users import --connection "Username-Password-Authentication" --users-body "[]"
auth0 users import --connection "Username-Password-Authentication" --users-body "$(cat path/to/users.json)"
auth0 users import -c "Username-Password-Authentication" --template "Basic Example"
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert true
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert true --email-results false
auth0 users import -c "Username-Password-Authentication" --users-body "$(cat path/to/users.json)" --upsert --email-results
auth0 users import -c "Username-Password-Authentication" --users-body "$(cat path/to/users.json)" --upsert --email-results --no-input
auth0 users import -c "Username-Password-Authentication" -b "$(cat path/to/users.json)" -u -r
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert --email-results
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert=false --email-results=false
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" -u=false -r=false
```


Expand All @@ -29,8 +35,9 @@ auth0 users import [flags]
```
-c, --connection string Name of the database connection this user should be created in.
-r, --email-results When true, sends a completion email to all tenant owners when the job is finished. The default is true, so you must explicitly set this parameter to false if you do not want emails sent. (default true)
-t, --template string Name of JSON example to be used.
-t, --template string Name of JSON example to be used. Cannot be used if the '--users-body' flag is passed. Options include: 'Empty', 'Basic Example', 'Custom Password Hash Example' and 'MFA Factors Example'.
-u, --upsert When set to false, pre-existing users that match on email address, user ID, or username will fail. When set to true, pre-existing users that match on any of these fields will be updated, but only with upsertable attributes.
-b, --users-body string JSON template body that contains an array of user(s) to be imported. Cannot be used if the '--template' flag is passed.
```


Expand Down
145 changes: 75 additions & 70 deletions internal/cli/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,18 @@ var (
Help: "Number of users, that match the search criteria, to retrieve. Minimum 1, maximum 1000. If limit is hit, refine the search query.",
}
userImportTemplate = Flag{
Name: "Template",
LongForm: "template",
ShortForm: "t",
Help: "Name of JSON example to be used.",
Name: "Template",
LongForm: "template",
ShortForm: "t",
Help: "Name of JSON example to be used. Cannot be used if the '--users-body' flag is passed. " +
"Options include: 'Empty', 'Basic Example', 'Custom Password Hash Example' and 'MFA Factors Example'.",
IsRequired: false,
}
userImportTemplateBody = Flag{
Name: "Template Body",
LongForm: "template-body",
userImportBody = Flag{
Name: "Users Body",
LongForm: "users-body",
ShortForm: "b",
Help: "JSON template body that contains an array of user(s) to be imported.",
Help: "JSON template body that contains an array of user(s) to be imported. Cannot be used if the '--template' flag is passed.",
IsRequired: false,
}
userEmailResults = Flag{
Expand Down Expand Up @@ -229,9 +230,8 @@ func createUserCmd(cli *cli) *cobra.Command {
auth0 users create --name "John Doe" --email [email protected] --connection "Username-Password-Authentication" --username "example"
auth0 users create -n "John Doe" -e [email protected] -c "Username-Password-Authentication" -u "example" --json`,
RunE: func(cmd *cobra.Command, args []string) error {
// Select from the available connection types
// Users API currently support database connections
options, err := cli.connectionPickerOptions()
// Users API currently only supports database connections.
options, err := cli.dbConnectionPickerOptions()
if err != nil {
return err
}
Expand Down Expand Up @@ -537,7 +537,7 @@ func importUsersCmd(cli *cli) *cobra.Command {
Connection string
ConnectionID string
Template string
TemplateBody string
UsersBody string
Upsert bool
SendCompletionEmail bool
}
Expand All @@ -549,76 +549,82 @@ func importUsersCmd(cli *cli) *cobra.Command {
The file size limit for a bulk import is 500KB. You will need to start multiple imports if your data exceeds this size.`,
Example: ` auth0 users import
auth0 users import --connection "Username-Password-Authentication"
auth0 users import --connection "Username-Password-Authentication" --users-body "[]"
auth0 users import --connection "Username-Password-Authentication" --users-body "$(cat path/to/users.json)"
auth0 users import -c "Username-Password-Authentication" --template "Basic Example"
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert true
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert true --email-results false`,
auth0 users import -c "Username-Password-Authentication" --users-body "$(cat path/to/users.json)" --upsert --email-results
auth0 users import -c "Username-Password-Authentication" --users-body "$(cat path/to/users.json)" --upsert --email-results --no-input
auth0 users import -c "Username-Password-Authentication" -b "$(cat path/to/users.json)" -u -r
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert --email-results
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" --upsert=false --email-results=false
auth0 users import -c "Username-Password-Authentication" -t "Basic Example" -u=false -r=false`,
RunE: func(cmd *cobra.Command, args []string) error {
// Select from the available connection types
// Users API currently support database connections
options, optionsErr := cli.connectionPickerOptions()
if optionsErr != nil {
return optionsErr
// Users API currently only supports database connections.
dbConnectionOptions, err := cli.dbConnectionPickerOptions()
if err != nil {
return err
}

if err := userConnection.Select(cmd, &inputs.Connection, options, nil); err != nil {
if err := userConnection.Select(cmd, &inputs.Connection, dbConnectionOptions, nil); err != nil {
return err
}

// Get Connection ID
conn, connErr := cli.api.Connection.ReadByName(inputs.Connection)
if connErr != nil {
return fmt.Errorf("Connection does not exist: %w", connErr)
connection, err := cli.api.Connection.ReadByName(inputs.Connection)
if err != nil {
return fmt.Errorf("failed to find connection with name %q: %w", inputs.Connection, err)
}

inputs.ConnectionID = *conn.ID

// Present user with template options
if templateErr := userImportTemplate.Select(cmd, &inputs.Template, userImportOptions.labels(), nil); templateErr != nil {
return templateErr
}
inputs.ConnectionID = connection.GetID()

editorErr := userImportTemplateBody.OpenEditor(
cmd,
&inputs.TemplateBody,
userImportOptions.getValue(inputs.Template),
inputs.Template+".*.json",
cli.userImportEditorHint,
)
if editorErr != nil {
return fmt.Errorf("Failed to capture input from the editor: %w", editorErr)
}
if inputs.UsersBody == "" {
err := userImportTemplate.Select(cmd, &inputs.Template, userImportOptions.labels(), nil)
if err != nil {
return err
}

var confirmed bool
if confirmedErr := prompt.AskBool("Do you want to import these user(s)?", &confirmed, true); confirmedErr != nil {
return fmt.Errorf("Failed to capture prompt input: %w", confirmedErr)
if err := userImportBody.OpenEditor(
cmd,
&inputs.UsersBody,
userImportOptions.getValue(inputs.Template),
inputs.Template+".*.json",
cli.userImportEditorHint,
); err != nil {
return fmt.Errorf("failed to capture input from the editor: %w", err)
}
}

if !confirmed {
return nil
if canPrompt(cmd) {
var confirmed bool
if err := prompt.AskBool("Do you want to import these user(s)?", &confirmed, true); err != nil {
return fmt.Errorf("failed to capture prompt input: %w", err)
}
if !confirmed {
return nil
}
}

// Convert json array to map
jsonstr := inputs.TemplateBody
var jsonmap []map[string]interface{}
jsonErr := json.Unmarshal([]byte(jsonstr), &jsonmap)
if jsonErr != nil {
return fmt.Errorf("Invalid JSON input: %w", jsonErr)
var usersBody []map[string]interface{}
if err := json.Unmarshal([]byte(inputs.UsersBody), &usersBody); err != nil {
return fmt.Errorf("invalid JSON input: %w", err)
}

err := ansi.Waiting(func() error {
return cli.api.Jobs.ImportUsers(&management.Job{
ConnectionID: &inputs.ConnectionID,
Users: jsonmap,
Upsert: &inputs.Upsert,
SendCompletionEmail: &inputs.SendCompletionEmail,
})
})
if err != nil {
if err := ansi.Waiting(func() error {
return cli.api.Jobs.ImportUsers(
&management.Job{
ConnectionID: &inputs.ConnectionID,
Users: usersBody,
Upsert: &inputs.Upsert,
SendCompletionEmail: &inputs.SendCompletionEmail,
},
)
}); err != nil {
return err
}

cli.renderer.Heading("starting user import job...")
fmt.Println(jsonstr)
cli.renderer.JSONResult(usersBody)
cli.renderer.Newline()
cli.renderer.Newline()

if inputs.SendCompletionEmail {
cli.renderer.Infof("Results of your user import job will be sent to your email.")
Expand All @@ -630,8 +636,10 @@ The file size limit for a bulk import is 500KB. You will need to start multiple

userConnection.RegisterString(cmd, &inputs.Connection, "")
userImportTemplate.RegisterString(cmd, &inputs.Template, "")
userImportBody.RegisterString(cmd, &inputs.UsersBody, "")
userEmailResults.RegisterBool(cmd, &inputs.SendCompletionEmail, true)
userImportUpsert.RegisterBool(cmd, &inputs.Upsert, false)
cmd.MarkFlagsMutuallyExclusive("template", "users-body")

return cmd
}
Expand All @@ -643,17 +651,15 @@ func formatUserDetailsPath(id string) string {
return fmt.Sprintf("users/%s", id)
}

func (c *cli) connectionPickerOptions() ([]string, error) {
var res []string

list, err := c.api.Connection.List()
func (c *cli) dbConnectionPickerOptions() ([]string, error) {
list, err := c.api.Connection.List(management.Parameter("strategy", management.ConnectionStrategyAuth0))
if err != nil {
return nil, err
}

var res []string
for _, conn := range list.Connections {
if conn.GetStrategy() == "auth0" {
res = append(res, conn.GetName())
}
res = append(res, conn.GetName())
}

if len(res) == 0 {
Expand All @@ -666,9 +672,8 @@ func (c *cli) connectionPickerOptions() ([]string, error) {
func (c *cli) getUserConnection(users *management.User) []string {
var res []string
for _, i := range users.Identities {
res = append(res, fmt.Sprintf("%v", auth0.StringValue(i.Connection)))
res = append(res, i.GetConnection())
}

return res
}

Expand Down
21 changes: 1 addition & 20 deletions internal/cli/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,6 @@ func TestConnectionsPickerOptions(t *testing.T) {
assert.ErrorContains(t, err, "There are currently no database connections.")
},
},
{
name: "no database connections",
connections: []*management.Connection{
{
Name: auth0.String("some-name-1"),
Strategy: auth0.String("foo"),
},
{
Name: auth0.String("some-name-2"),
Strategy: auth0.String("foo"),
},
},
assertOutput: func(t testing.TB, options []string) {
t.Fail()
},
assertError: func(t testing.TB, err error) {
assert.ErrorContains(t, err, "There are currently no database connections.")
},
},
{
name: "API error",
apiError: errors.New("error"),
Expand Down Expand Up @@ -98,7 +79,7 @@ func TestConnectionsPickerOptions(t *testing.T) {
api: &auth0.API{Connection: connectionAPI},
}

options, err := cli.connectionPickerOptions()
options, err := cli.dbConnectionPickerOptions()

if err != nil {
test.assertError(t, err)
Expand Down
10 changes: 10 additions & 0 deletions test/integration/users-test-cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,13 @@ tests:
stderr:
contains:
- "Open the following URL in a browser: https://manage.auth0.com/dashboard/"

016 - users import:
command: auth0 users import -c "Username-Password-Authentication" -b "[]" -r=false --no-input
exit-code: 0
stdout:
contains:
- "[]"
stderr:
contains:
- "starting user import job..."

0 comments on commit 538b6bf

Please sign in to comment.