Merge pull request #16537 from influxdata/cli_update_password

feat(cmd/influx): update password in cli
pull/16553/head
kelwang 2020-01-15 14:17:20 -05:00 committed by GitHub
commit 540e785eb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 622 additions and 141 deletions

View File

@ -44,6 +44,7 @@
1. [16509](https://github.com/influxdata/influxdb/pull/16509): Add support for applying an influx package via a public facing URL
1. [16511](https://github.com/influxdata/influxdb/pull/16511): Add jsonnet support for influx packages
1. [14782](https://github.com/influxdata/influxdb/pull/16336): Add view page for Check
1. [16537](https://github.com/influxdata/influxdb/pull/16537): Add update password for CLI
### Bug Fixes
@ -68,6 +69,7 @@
1. [16491](https://github.com/influxdata/influxdb/pull/16491): Add missing env vals to influx cli usage and fixes precedence of flag/env var priority
### UI Improvements
1. [16444](https://github.com/influxdata/influxdb/pull/16444): Add honeybadger reporting to create checks
## v2.0.0-alpha.21 [2019-12-13]

View File

@ -7,6 +7,7 @@ import (
"os"
"reflect"
"testing"
"io/ioutil"
"time"
"github.com/influxdata/influxdb"
@ -102,7 +103,7 @@ func TestCmdBucket(t *testing.T) {
return nil
}
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdCreate()
cmd.RunE = builder.cmdCreateRunEFn
return cmd
@ -151,7 +152,7 @@ func TestCmdBucket(t *testing.T) {
return nil
}
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdDelete()
cmd.RunE = builder.cmdDeleteRunEFn
return cmd
@ -258,7 +259,7 @@ func TestCmdBucket(t *testing.T) {
return nil, 0, nil
}
builder := newCmdBucketBuilder(fakeSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
builder := newCmdBucketBuilder(fakeSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdFind()
cmd.RunE = builder.cmdFindRunEFn
return cmd, calls
@ -352,7 +353,7 @@ func TestCmdBucket(t *testing.T) {
return &influxdb.Bucket{}, nil
}
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdBucketBuilder(fakeSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdUpdate()
cmd.RunE = builder.cmdUpdateRunEFn
return cmd

View File

@ -128,7 +128,7 @@ func influxCmd(opts ...genericCLIOptFn) *cobra.Command {
cmdREPL(),
cmdSetup(),
cmdTask(),
cmdUser(),
cmdUser(runEWrapper),
cmdWrite(),
)

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"io/ioutil"
"reflect"
"testing"
@ -63,7 +64,7 @@ func TestCmdOrg(t *testing.T) {
return nil
}
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdCreate()
return cmd
}
@ -109,7 +110,7 @@ func TestCmdOrg(t *testing.T) {
return nil
}
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdDelete()
return cmd
}
@ -181,7 +182,7 @@ func TestCmdOrg(t *testing.T) {
return nil, 0, nil
}
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdFind()
return cmd, calls
}
@ -268,7 +269,7 @@ func TestCmdOrg(t *testing.T) {
return &influxdb.Organization{}, nil
}
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), out(ioutil.Discard))
cmd := builder.cmdUpdate()
return cmd
}
@ -366,7 +367,7 @@ func TestCmdOrg(t *testing.T) {
return &influxdb.Organization{ID: 1}, nil
}
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgSVCFn(svc), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdMemberList()
return cmd, calls
}
@ -394,7 +395,7 @@ func TestCmdOrg(t *testing.T) {
return nil
}
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdMemberAdd()
return cmd, calls
}
@ -457,7 +458,7 @@ func TestCmdOrg(t *testing.T) {
return nil
}
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(new(bytes.Buffer)))
builder := newCmdOrgBuilder(fakeOrgUrmSVCsFn(svc, urmSVC), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdMemberRemove()
return cmd, calls
}

View File

@ -88,7 +88,7 @@ func setupF(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to write token to path %q: %v", dPath, err)
}
fmt.Println(promptWithColor("Your token has been stored in "+dPath+".", colorCyan))
fmt.Println(string(promptWithColor("Your token has been stored in "+dPath+".", colorCyan)))
w := internal.NewTabWriter(os.Stdout)
w.WriteHeaders(
@ -144,7 +144,7 @@ func interactive() (req *platform.OnboardingRequest, err error) {
Reader: os.Stdin,
}
req = new(platform.OnboardingRequest)
fmt.Println(promptWithColor(`Welcome to InfluxDB 2.0!`, colorYellow))
fmt.Println(string(promptWithColor(`Welcome to InfluxDB 2.0!`, colorYellow)))
if setupFlags.username != "" {
req.User = setupFlags.username
} else {
@ -153,7 +153,7 @@ func interactive() (req *platform.OnboardingRequest, err error) {
if setupFlags.password != "" {
req.Password = setupFlags.password
} else {
req.Password = getPassword(ui)
req.Password = getPassword(ui, false)
}
if setupFlags.token != "" {
req.Token = setupFlags.token
@ -200,8 +200,9 @@ var (
keyReset = []byte{keyEscape, '[', '0', 'm'}
)
func promptWithColor(s string, color []byte) string {
return string(color) + s + string(keyReset)
func promptWithColor(s string, color []byte) []byte {
bb := append(color, []byte(s)...)
return append(bb, keyReset...)
}
func getConfirm(ui *input.UI, or *platform.OnboardingRequest) bool {
@ -211,14 +212,14 @@ func getConfirm(ui *input.UI, or *platform.OnboardingRequest) bool {
if or.RetentionPeriod > 0 {
rp = fmt.Sprintf("%d hrs", or.RetentionPeriod)
}
fmt.Print(promptWithColor(fmt.Sprintf(`
ui.Writer.Write(promptWithColor(fmt.Sprintf(`
You have entered:
Username: %s
Organization: %s
Bucket: %s
Retention Period: %s
`, or.User, or.Org, or.Bucket, rp), colorCyan))
result, err := ui.Ask(prompt, &input.Options{
result, err := ui.Ask(string(prompt), &input.Options{
HideOrder: true,
})
if err != nil {
@ -240,15 +241,20 @@ var (
errPasswordIsTooShort = fmt.Errorf("passwords is too short")
)
func getPassword(ui *input.UI) (password string) {
func getPassword(ui *input.UI, showNew bool) (password string) {
newStr := ""
if showNew {
newStr = " new"
}
var err error
enterPasswd:
query := promptWithColor("Please type your password", colorCyan)
query := string(promptWithColor("Please type your"+newStr+" password", colorCyan))
for {
password, err = ui.Ask(query, &input.Options{
Required: true,
HideOrder: true,
Hide: true,
Mask: false,
ValidateFunc: func(s string) error {
if len(s) < 8 {
return errPasswordIsTooShort
@ -260,7 +266,7 @@ enterPasswd:
case input.ErrInterrupted:
os.Exit(1)
case errPasswordIsTooShort:
fmt.Println(promptWithColor("Password too short - minimum length is 8 characters!", colorRed))
ui.Writer.Write(promptWithColor("Password too short - minimum length is 8 characters!", colorRed))
goto enterPasswd
default:
if password = strings.TrimSpace(password); password == "" {
@ -269,7 +275,7 @@ enterPasswd:
}
break
}
query = promptWithColor("Please type your password again", colorCyan)
query = string(promptWithColor("Please type your"+newStr+" password again", colorCyan))
for {
_, err = ui.Ask(query, &input.Options{
Required: true,
@ -288,7 +294,7 @@ enterPasswd:
case nil:
// Nothing.
default:
fmt.Println(promptWithColor("Passwords do not match!", colorRed))
ui.Writer.Write(promptWithColor("Passwords do not match!\n", colorRed))
goto enterPasswd
}
break
@ -305,7 +311,7 @@ func getInput(ui *input.UI, prompt, defaultValue string) string {
option.Default = defaultValue
option.HideDefault = true
}
prompt = promptWithColor(prompt, colorCyan)
prompt = string(promptWithColor(prompt, colorCyan))
for {
line, err := ui.Ask(prompt, option)
switch err {

View File

@ -3,50 +3,96 @@ package main
import (
"context"
"errors"
"fmt"
"os"
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/cmd/influx/internal"
"github.com/influxdata/influxdb/http"
"github.com/spf13/cobra"
input "github.com/tcnksm/go-input"
)
func cmdUser() *cobra.Command {
cmd := &cobra.Command{
Use: "user",
Short: "User management commands",
Run: seeHelp,
type userSVCsFn func() (
cmdUserDeps,
error,
)
type cmdUserDeps struct {
userSVC influxdb.UserService
orgSvc influxdb.OrganizationService
passSVC influxdb.PasswordsService
urmSVC influxdb.UserResourceMappingService
getPassFn func(*input.UI, bool) string
}
func cmdUser(opts ...genericCLIOptFn) *cobra.Command {
return newCmdUserBuilder(newUserSVC, opts...).cmd()
}
type cmdUserBuilder struct {
genericCLIOpts
svcFn userSVCsFn
id string
name string
password string
org organization
}
func newCmdUserBuilder(svcsFn userSVCsFn, opts ...genericCLIOptFn) *cmdUserBuilder {
opt := genericCLIOpts{
in: os.Stdin,
w: os.Stdout,
}
for _, o := range opts {
o(&opt)
}
return &cmdUserBuilder{
genericCLIOpts: opt,
svcFn: svcsFn,
}
}
func (b *cmdUserBuilder) cmd() *cobra.Command {
cmd := b.newCmd("user", nil)
cmd.Short = "User management commands"
cmd.Run = seeHelp
cmd.AddCommand(
userCreateCmd(),
userDeleteCmd(),
userFindCmd(),
userUpdateCmd(),
b.cmdCreate(),
b.cmdDelete(),
b.cmdFind(),
b.cmdUpdate(),
b.cmdPassword(),
)
return cmd
}
var userUpdateFlags struct {
id string
name string
func (b *cmdUserBuilder) cmdPassword() *cobra.Command {
cmd := b.newCmd("password", b.cmdPasswordRunEFn)
cmd.Short = "Update user password"
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID")
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
return cmd
}
func userUpdateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update",
Short: "Update user",
RunE: wrapCheckSetup(userUpdateF),
}
func (b *cmdUserBuilder) cmdUpdate() *cobra.Command {
cmd := b.newCmd("update", b.cmdUpdateRunEFn)
cmd.Short = "Update user"
cmd.Flags().StringVarP(&userUpdateFlags.id, "id", "i", "", "The user ID (required)")
cmd.Flags().StringVarP(&userUpdateFlags.name, "name", "n", "", "The user name")
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID (required)")
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
cmd.MarkFlagRequired("id")
return cmd
}
func newUserService() (platform.UserService, error) {
func newUserService() (influxdb.UserService, error) {
if flags.local {
return newLocalKVService()
}
@ -60,28 +106,85 @@ func newUserService() (platform.UserService, error) {
}, nil
}
func userUpdateF(cmd *cobra.Command, args []string) error {
s, err := newUserService()
func newUserSVC() (
cmdUserDeps,
error) {
httpClient, err := newHTTPClient()
if err != nil {
return cmdUserDeps{}, err
}
userSvc := &http.UserService{Client: httpClient}
orgSvc := &http.OrganizationService{Client: httpClient}
passSvc := &http.PasswordService{Client: httpClient}
urmSvc := &http.UserResourceMappingService{Client: httpClient}
getPassFn := getPassword
return cmdUserDeps{
userSVC: userSvc,
orgSvc: orgSvc,
passSVC: passSvc,
urmSVC: urmSvc,
getPassFn: getPassFn,
}, nil
}
func (b *cmdUserBuilder) cmdPasswordRunEFn(cmd *cobra.Command, args []string) error {
ctx := context.Background()
dep, err := b.svcFn()
if err != nil {
return err
}
var id platform.ID
if err := id.DecodeFromString(userUpdateFlags.id); err != nil {
filter := influxdb.UserFilter{}
if b.name != "" {
filter.Name = &b.name
}
if b.id != "" {
id, err := influxdb.IDFromString(b.id)
if err != nil {
return err
}
filter.ID = id
}
u, err := dep.userSVC.FindUser(ctx, filter)
if err != nil {
return err
}
update := platform.UserUpdate{}
if userUpdateFlags.name != "" {
update.Name = &userUpdateFlags.name
ui := &input.UI{
Writer: b.genericCLIOpts.w,
Reader: b.genericCLIOpts.in,
}
password := dep.getPassFn(ui, true)
user, err := s.UpdateUser(context.Background(), id, update)
if err = dep.passSVC.SetPassword(ctx, u.ID, password); err != nil {
return err
}
fmt.Fprintln(b.w, "Your password has been successfully updated.")
return nil
}
func (b *cmdUserBuilder) cmdUpdateRunEFn(cmd *cobra.Command, args []string) error {
dep, err := b.svcFn()
if err != nil {
return err
}
w := internal.NewTabWriter(os.Stdout)
var id influxdb.ID
if err := id.DecodeFromString(b.id); err != nil {
return err
}
update := influxdb.UserUpdate{}
if b.name != "" {
update.Name = &b.name
}
user, err := dep.userSVC.UpdateUser(context.Background(), id, update)
if err != nil {
return err
}
w := internal.NewTabWriter(b.w)
w.WriteHeaders(
"ID",
"Name",
@ -95,42 +198,43 @@ func userUpdateF(cmd *cobra.Command, args []string) error {
return nil
}
var userCreateFlags struct {
name string
password string
org organization
}
func (b *cmdUserBuilder) cmdCreate() *cobra.Command {
cmd := b.newCmd("create", b.cmdCreateRunEFn)
cmd.Short = "Create user"
func userCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create user",
RunE: wrapCheckSetup(userCreateF),
opts := flagOpts{
{
DestP: &b.name,
Flag: "name",
Short: 'n',
Desc: "The user name (required)",
Required: true,
},
}
opts.mustRegister(cmd)
userCreateFlags.org.register(cmd, false)
cmd.Flags().StringVarP(&userCreateFlags.name, "name", "n", "", "The user name (required)")
cmd.MarkFlagRequired("name")
cmd.Flags().StringVarP(&userCreateFlags.password, "password", "p", "", "The user password")
cmd.Flags().StringVarP(&b.password, "password", "p", "", "The user password")
b.org.register(cmd, false)
return cmd
}
func userCreateF(cmd *cobra.Command, args []string) error {
if err := userCreateFlags.org.validOrgFlags(); err != nil {
func (b *cmdUserBuilder) cmdCreateRunEFn(*cobra.Command, []string) error {
ctx := context.Background()
if err := b.org.validOrgFlags(); err != nil {
return err
}
s, err := newUserService()
dep, err := b.svcFn()
if err != nil {
return err
}
user := &platform.User{
Name: userCreateFlags.name,
user := &influxdb.User{
Name: b.name,
}
if err := s.CreateUser(context.Background(), user); err != nil {
if err := dep.userSVC.CreateUser(ctx, user); err != nil {
return err
}
@ -143,7 +247,7 @@ func userCreateF(cmd *cobra.Command, args []string) error {
for i, h := range headers {
m[h] = vals[i]
}
w := internal.NewTabWriter(os.Stdout)
w := internal.NewTabWriter(b.w)
w.WriteHeaders(headers...)
w.Write(m)
w.Flush()
@ -151,17 +255,12 @@ func userCreateF(cmd *cobra.Command, args []string) error {
return nil
}
orgSVC, err := newOrganizationService()
orgID, err := b.org.getID(dep.orgSvc)
if err != nil {
return err
}
orgID, err := userCreateFlags.org.getID(orgSVC)
if err != nil {
return err
}
pass := userCreateFlags.password
pass := b.password
if orgID == 0 && pass == "" {
return writeOutput([]string{"ID", "Name"}, user.ID.String(), user.Name)
}
@ -170,77 +269,57 @@ func userCreateF(cmd *cobra.Command, args []string) error {
return errors.New("an org id is required when providing a user password")
}
c, err := newHTTPClient()
if err != nil {
return err
}
userResMapSVC := &http.UserResourceMappingService{
Client: c,
}
err = userResMapSVC.CreateUserResourceMapping(context.Background(), &platform.UserResourceMapping{
err = dep.urmSVC.CreateUserResourceMapping(context.Background(), &influxdb.UserResourceMapping{
UserID: user.ID,
UserType: platform.Member,
ResourceType: platform.OrgsResourceType,
UserType: influxdb.Member,
ResourceType: influxdb.OrgsResourceType,
ResourceID: orgID,
})
if err != nil {
return err
}
passSVC := &http.PasswordService{Client: c}
ctx := context.Background()
if err := passSVC.SetPassword(ctx, user.ID, pass); err != nil {
if err := dep.passSVC.SetPassword(ctx, user.ID, pass); err != nil {
return err
}
return writeOutput([]string{"ID", "Name", "Organization ID"}, user.ID.String(), user.Name, orgID.String())
}
var userFindFlags struct {
id string
name string
}
func (b *cmdUserBuilder) cmdFind() *cobra.Command {
cmd := b.newCmd("find", b.cmdFindRunEFn)
cmd.Short = "Find user"
func userFindCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "find",
Short: "Find user",
RunE: wrapCheckSetup(userFindF),
}
cmd.Flags().StringVarP(&userFindFlags.id, "id", "i", "", "The user ID")
cmd.Flags().StringVarP(&userFindFlags.name, "name", "n", "", "The user name")
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID")
cmd.Flags().StringVarP(&b.name, "name", "n", "", "The user name")
return cmd
}
func userFindF(cmd *cobra.Command, args []string) error {
s, err := newUserService()
func (b *cmdUserBuilder) cmdFindRunEFn(*cobra.Command, []string) error {
dep, err := b.svcFn()
if err != nil {
return err
}
filter := platform.UserFilter{}
if userFindFlags.name != "" {
filter.Name = &userFindFlags.name
filter := influxdb.UserFilter{}
if b.name != "" {
filter.Name = &b.name
}
if userFindFlags.id != "" {
id, err := platform.IDFromString(userFindFlags.id)
if b.id != "" {
id, err := influxdb.IDFromString(b.id)
if err != nil {
return err
}
filter.ID = id
}
users, _, err := s.FindUsers(context.Background(), filter)
users, _, err := dep.userSVC.FindUsers(context.Background(), filter)
if err != nil {
return err
}
w := internal.NewTabWriter(os.Stdout)
w := internal.NewTabWriter(b.w)
w.WriteHeaders(
"ID",
"Name",
@ -256,45 +335,38 @@ func userFindF(cmd *cobra.Command, args []string) error {
return nil
}
var userDeleteFlags struct {
id string
}
func (b *cmdUserBuilder) cmdDelete() *cobra.Command {
cmd := b.newCmd("delete", b.cmdDeleteRunEFn)
cmd.Short = "Delete user"
func userDeleteCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete user",
RunE: wrapCheckSetup(userDeleteF),
}
cmd.Flags().StringVarP(&userDeleteFlags.id, "id", "i", "", "The user ID (required)")
cmd.Flags().StringVarP(&b.id, "id", "i", "", "The user ID (required)")
cmd.MarkFlagRequired("id")
return cmd
}
func userDeleteF(cmd *cobra.Command, args []string) error {
s, err := newUserService()
func (b *cmdUserBuilder) cmdDeleteRunEFn(cmd *cobra.Command, args []string) error {
dep, err := b.svcFn()
if err != nil {
return err
}
var id platform.ID
if err := id.DecodeFromString(userDeleteFlags.id); err != nil {
var id influxdb.ID
if err := id.DecodeFromString(b.id); err != nil {
return err
}
ctx := context.Background()
u, err := s.FindUserByID(ctx, id)
u, err := dep.userSVC.FindUserByID(ctx, id)
if err != nil {
return err
}
if err := s.DeleteUser(ctx, id); err != nil {
if err := dep.userSVC.DeleteUser(ctx, id); err != nil {
return err
}
w := internal.NewTabWriter(os.Stdout)
w := internal.NewTabWriter(b.w)
w.WriteHeaders(
"ID",
"Name",

399
cmd/influx/user_test.go Normal file
View File

@ -0,0 +1,399 @@
package main
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"reflect"
"testing"
"github.com/influxdata/influxdb"
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/mock"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
input "github.com/tcnksm/go-input"
)
func newCMDUserDeps(
userSVC influxdb.UserService,
passSVC influxdb.PasswordsService,
getPassFn func(*input.UI, bool) string,
) cmdUserDeps {
return cmdUserDeps{
userSVC: userSVC,
orgSvc: &mock.OrganizationService{
FindOrganizationF: func(ctx context.Context, filter influxdb.OrganizationFilter) (*influxdb.Organization, error) {
return &influxdb.Organization{ID: influxdb.ID(9000), Name: "influxdata"}, nil
},
},
passSVC: passSVC,
urmSVC: &mock.UserResourceMappingService{
CreateMappingFn: func(context.Context, *platform.UserResourceMapping) error {
return nil
},
},
getPassFn: getPassFn,
}
}
func TestCmdUser(t *testing.T) {
setViperOptions()
type userResult struct {
user influxdb.User
password string
}
fakeSVCFn := func(dep cmdUserDeps) userSVCsFn {
return func() (
cmdUserDeps,
error) {
return dep, nil
}
}
t.Run("create", func(t *testing.T) {
tests := []struct {
name string
expected userResult
flags []string
envVars map[string]string
}{
{
name: "basic just name",
flags: []string{"--name=new name", "--org=org name"},
expected: userResult{
user: influxdb.User{
Name: "new name",
},
},
},
{
name: "with password",
flags: []string{
"--name=new name",
"--password=pass1",
"--org=org name",
},
expected: userResult{
user: influxdb.User{
Name: "new name",
},
password: "pass1",
},
},
{
name: "with password and env",
flags: []string{
"--name=new name",
"--password=pass1",
},
envVars: map[string]string{
"INFLUX_ORG_ID": influxdb.ID(1).String(),
},
expected: userResult{
user: influxdb.User{
Name: "new name",
},
password: "pass1",
},
},
{
name: "shorts",
flags: []string{
"-n=new name",
"-o=org name",
},
expected: userResult{
user: influxdb.User{
Name: "new name",
},
},
},
}
cmdFn := func(expected userResult) *cobra.Command {
svc := mock.NewUserService()
svc.CreateUserFn = func(ctx context.Context, User *influxdb.User) error {
if expected.user != *User {
return fmt.Errorf("unexpected User;\n\twant= %+v\n\tgot= %+v", expected, *User)
}
return nil
}
passSVC := mock.NewPasswordsService()
passSVC.SetPasswordFn = func(ctx context.Context, id influxdb.ID, password string) error {
if expected.password != password {
return fmt.Errorf("unexpected password;\n\twant= %+v\n\tgot= %+v", expected.password, password)
}
return nil
}
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, passSVC, nil)), out(ioutil.Discard))
cmd := builder.cmdCreate()
cmd.RunE = builder.cmdCreateRunEFn
return cmd
}
for _, tt := range tests {
fn := func(t *testing.T) {
defer addEnvVars(t, tt.envVars)()
cmd := cmdFn(tt.expected)
cmd.LocalFlags().Parse(tt.flags)
err := cmd.Execute()
require.NoError(t, err)
}
t.Run(tt.name, fn)
}
})
t.Run("delete", func(t *testing.T) {
tests := []struct {
name string
expectedID influxdb.ID
flag string
}{
{
name: "long id",
expectedID: influxdb.ID(1),
flag: "--id=",
},
{
name: "shorts",
expectedID: influxdb.ID(1),
flag: "-i=",
},
}
cmdFn := func(expectedID influxdb.ID) *cobra.Command {
svc := mock.NewUserService()
svc.FindUserByIDFn = func(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id}, nil
}
svc.DeleteUserFn = func(ctx context.Context, id influxdb.ID) error {
if expectedID != id {
return fmt.Errorf("unexpected id:\n\twant= %s\n\tgot= %s", expectedID, id)
}
return nil
}
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, nil)), out(ioutil.Discard))
cmd := builder.cmdDelete()
cmd.RunE = builder.cmdDeleteRunEFn
return cmd
}
for _, tt := range tests {
fn := func(t *testing.T) {
cmd := cmdFn(tt.expectedID)
idFlag := tt.flag + tt.expectedID.String()
cmd.LocalFlags().Parse([]string{idFlag})
require.NoError(t, cmd.Execute())
}
t.Run(tt.name, fn)
}
})
t.Run("find", func(t *testing.T) {
type called struct {
name string
id influxdb.ID
}
tests := []struct {
name string
expected called
flags []string
}{
{
name: "id",
flags: []string{
"--id=" + influxdb.ID(2).String(),
},
expected: called{
id: 2,
},
},
{
name: "name",
flags: []string{"--name=name1"},
expected: called{name: "name1"},
},
{
name: "shorts",
flags: []string{
"-n=name1",
"-i=" + influxdb.ID(1).String(),
},
expected: called{name: "name1", id: 1},
},
}
cmdFn := func() (*cobra.Command, *called) {
calls := new(called)
svc := mock.NewUserService()
svc.FindUsersFn = func(ctx context.Context, f influxdb.UserFilter, opt ...influxdb.FindOptions) ([]*influxdb.User, int, error) {
if f.ID != nil {
calls.id = *f.ID
}
if f.Name != nil {
calls.name = *f.Name
}
return nil, 0, nil
}
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, nil)), in(new(bytes.Buffer)), out(ioutil.Discard))
cmd := builder.cmdFind()
cmd.RunE = builder.cmdFindRunEFn
return cmd, calls
}
for _, tt := range tests {
fn := func(t *testing.T) {
cmd, calls := cmdFn()
cmd.LocalFlags().Parse(tt.flags)
require.NoError(t, cmd.Execute())
assert.Equal(t, tt.expected, *calls)
}
t.Run(tt.name, fn)
}
})
t.Run("update", func(t *testing.T) {
tests := []struct {
name string
expected influxdb.UserUpdate
flags []string
}{
{
name: "basic just name",
flags: []string{
"--id=" + influxdb.ID(3).String(),
"--name=new name",
},
expected: influxdb.UserUpdate{
Name: strPtr("new name"),
},
},
{
name: "with all fields",
flags: []string{
"--id=" + influxdb.ID(3).String(),
"--name=new name",
},
expected: influxdb.UserUpdate{
Name: strPtr("new name"),
},
},
{
name: "shorts",
flags: []string{
"-i=" + influxdb.ID(3).String(),
"-n=new name",
},
expected: influxdb.UserUpdate{
Name: strPtr("new name"),
},
},
}
cmdFn := func(expectedUpdate influxdb.UserUpdate) *cobra.Command {
svc := mock.NewUserService()
svc.UpdateUserFn = func(ctx context.Context, id influxdb.ID, upd influxdb.UserUpdate) (*influxdb.User, error) {
if id != 3 {
return nil, fmt.Errorf("unexpecte id:\n\twant= %s\n\tgot= %s", influxdb.ID(3), id)
}
if !reflect.DeepEqual(expectedUpdate, upd) {
return nil, fmt.Errorf("unexpected User update;\n\twant= %+v\n\tgot= %+v", expectedUpdate, upd)
}
return &influxdb.User{}, nil
}
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, nil, nil)), out(ioutil.Discard))
cmd := builder.cmdUpdate()
cmd.RunE = builder.cmdUpdateRunEFn
return cmd
}
for _, tt := range tests {
fn := func(t *testing.T) {
cmd := cmdFn(tt.expected)
cmd.LocalFlags().Parse(tt.flags)
require.NoError(t, cmd.Execute())
}
t.Run(tt.name, fn)
}
})
t.Run("password", func(t *testing.T) {
tests := []struct {
name string
expected string
flags []string
}{
{
name: "basic id",
flags: []string{
"--id=" + influxdb.ID(3).String(),
},
expected: "pass1",
},
{
name: "shorts",
flags: []string{
"-i=" + influxdb.ID(3).String(),
"-n=new name",
},
expected: "pass1",
},
}
cmdFn := func(expected string) *cobra.Command {
svc := mock.NewUserService()
svc.FindUserFn = func(ctx context.Context, f influxdb.UserFilter) (*influxdb.User, error) {
usr := new(influxdb.User)
if id := f.ID; id != nil {
usr.ID = *id
}
if name := f.Name; name != nil {
usr.Name = *name
}
return usr, nil
}
passSVC := mock.NewPasswordsService()
passSVC.SetPasswordFn = func(ctx context.Context, id influxdb.ID, pass string) error {
if pass != expected {
return fmt.Errorf("unexpecte id:\n\twant= %s\n\tgot= %s", pass, expected)
}
return nil
}
getPassFn := func(*input.UI, bool) string {
return expected
}
builder := newCmdUserBuilder(fakeSVCFn(newCMDUserDeps(svc, passSVC, getPassFn)),
out(ioutil.Discard))
cmd := builder.cmdPassword()
cmd.RunE = builder.cmdPasswordRunEFn
return cmd
}
for _, tt := range tests {
fn := func(t *testing.T) {
cmd := cmdFn(tt.expected)
cmd.LocalFlags().Parse(tt.flags)
require.NoError(t, cmd.Execute())
}
t.Run(tt.name, fn)
}
})
}