Add Num to UsersStore interface

pull/10616/head
Michael Desa 2017-11-30 12:55:59 -05:00
parent 5dc7444678
commit ff9dd97026
11 changed files with 443 additions and 0 deletions

View File

@ -51,6 +51,24 @@ func (s *UsersStore) each(ctx context.Context, fn func(*chronograf.User)) error
})
}
// Num returns the number of users in the UsersStore
func (s *UsersStore) Num(ctx context.Context) (int, error) {
count := 0
err := s.client.db.View(func(tx *bolt.Tx) error {
return tx.Bucket(UsersBucket).ForEach(func(k, v []byte) error {
count++
return nil
})
})
if err != nil {
return 0, err
}
return count, nil
}
// Get searches the UsersStore for user with name
func (s *UsersStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
if q.ID != nil {

View File

@ -522,3 +522,67 @@ func TestUsersStore_All(t *testing.T) {
}
}
}
func TestUsersStore_Num(t *testing.T) {
tests := []struct {
name string
ctx context.Context
users []chronograf.User
want int
wantErr bool
}{
{
name: "No users",
want: 0,
},
{
name: "Update new user",
want: 2,
users: []chronograf.User{
{
Name: "howdy",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Name: "viewer",
},
},
},
{
Name: "doody",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Name: "editor",
},
},
},
},
},
}
for _, tt := range tests {
client, err := NewTestClient()
if err != nil {
t.Fatal(err)
}
if err := client.Open(context.TODO()); err != nil {
t.Fatal(err)
}
defer client.Close()
s := client.UsersStore
for _, u := range tt.users {
s.Add(tt.ctx, &u)
}
got, err := s.Num(tt.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("%q. UsersStore.Num() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if got != tt.want {
t.Errorf("%q. UsersStore.Num() = %d. want %d", tt.name, got, tt.want)
}
}
}

View File

@ -662,6 +662,8 @@ type UsersStore interface {
Get(ctx context.Context, q UserQuery) (*User, error)
// Update the user's permissions or roles
Update(context.Context, *User) error
// Num returns the number of users in the UsersStore
Num(context.Context) (int, error)
}
// Database represents a database in a time series source

View File

@ -37,6 +37,16 @@ func (c *UserStore) Delete(ctx context.Context, u *chronograf.User) error {
return c.Ctrl.DeleteUser(ctx, u.Name)
}
// Number of users in Influx
func (c *UserStore) Num(ctx context.Context) (int, error) {
all, err := c.All(ctx)
if err != nil {
return 0, err
}
return len(all), nil
}
// Get retrieves a user if name exists.
func (c *UserStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) {
if q.Name == nil {

View File

@ -534,6 +534,94 @@ func TestClient_Update(t *testing.T) {
}
}
func TestClient_Num(t *testing.T) {
type fields struct {
Ctrl *mockCtrl
Logger chronograf.Logger
}
type args struct {
ctx context.Context
}
tests := []struct {
name string
fields fields
args args
want []chronograf.User
wantErr bool
}{
{
name: "Successful Get User",
fields: fields{
Ctrl: &mockCtrl{
users: func(ctx context.Context, name *string) (*enterprise.Users, error) {
return &enterprise.Users{
Users: []enterprise.User{
{
Name: "marty",
Password: "johnny be good",
Permissions: map[string][]string{
"": {
"ViewChronograf",
"ReadData",
"WriteData",
},
},
},
},
}, nil
},
userRoles: func(ctx context.Context) (map[string]enterprise.Roles, error) {
return map[string]enterprise.Roles{}, nil
},
},
},
args: args{
ctx: context.Background(),
},
want: []chronograf.User{
{
Name: "marty",
Permissions: chronograf.Permissions{
{
Scope: chronograf.AllScope,
Allowed: chronograf.Allowances{"ViewChronograf", "ReadData", "WriteData"},
},
},
Roles: []chronograf.Role{},
},
},
},
{
name: "Failure to get User",
fields: fields{
Ctrl: &mockCtrl{
users: func(ctx context.Context, name *string) (*enterprise.Users, error) {
return nil, fmt.Errorf("1.21 Gigawatts! Tom, how could I have been so careless?")
},
},
},
args: args{
ctx: context.Background(),
},
wantErr: true,
},
}
for _, tt := range tests {
c := &enterprise.UserStore{
Ctrl: tt.fields.Ctrl,
Logger: tt.fields.Logger,
}
got, err := c.Num(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("%q. Client.Num() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if got != len(tt.want) {
t.Errorf("%q. Client.Num() = %v, want %v", tt.name, got, len(tt.want))
}
}
}
func TestClient_All(t *testing.T) {
type fields struct {
Ctrl *mockCtrl

View File

@ -126,6 +126,16 @@ func (c *Client) All(ctx context.Context) ([]chronograf.User, error) {
return users, nil
}
// Number of users in Influx
func (c *Client) Num(ctx context.Context) (int, error) {
all, err := c.All(ctx)
if err != nil {
return 0, err
}
return len(all), nil
}
// showUsers runs SHOW USERS InfluxQL command and returns chronograf users.
func (c *Client) showUsers(ctx context.Context) ([]chronograf.User, error) {
res, err := c.Query(ctx, chronograf.Query{

View File

@ -573,6 +573,102 @@ func TestClient_revokePermission(t *testing.T) {
}
}
func TestClient_Num(t *testing.T) {
type args struct {
ctx context.Context
}
tests := []struct {
name string
args args
statusUsers int
showUsers []byte
statusGrants int
showGrants []byte
want []chronograf.User
wantErr bool
}{
{
name: "All Users",
statusUsers: http.StatusOK,
showUsers: []byte(`{"results":[{"series":[{"columns":["user","admin"],"values":[["admin",true],["docbrown",true],["reader",false]]}]}]}`),
statusGrants: http.StatusOK,
showGrants: []byte(`{"results":[{"series":[{"columns":["database","privilege"],"values":[["mydb","ALL PRIVILEGES"]]}]}]}`),
args: args{
ctx: context.Background(),
},
want: []chronograf.User{
{
Name: "admin",
Permissions: chronograf.Permissions{
chronograf.Permission{
Scope: "all",
Allowed: []string{"ALL"},
},
chronograf.Permission{
Scope: "database",
Name: "mydb",
Allowed: []string{"WRITE", "READ"},
},
},
},
{
Name: "docbrown",
Permissions: chronograf.Permissions{
chronograf.Permission{
Scope: "all",
Allowed: []string{"ALL"},
},
chronograf.Permission{
Scope: "database",
Name: "mydb",
Allowed: []string{"WRITE", "READ"},
},
},
},
{
Name: "reader",
Permissions: chronograf.Permissions{
chronograf.Permission{
Scope: "database",
Name: "mydb",
Allowed: []string{"WRITE", "READ"},
},
},
},
},
},
}
for _, tt := range tests {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if path := r.URL.Path; path != "/query" {
t.Error("Expected the path to contain `/query` but was", path)
}
query := r.URL.Query().Get("q")
if strings.Contains(query, "GRANTS") {
rw.WriteHeader(tt.statusGrants)
rw.Write(tt.showGrants)
} else if strings.Contains(query, "USERS") {
rw.WriteHeader(tt.statusUsers)
rw.Write(tt.showUsers)
}
}))
u, _ := url.Parse(ts.URL)
c := &Client{
URL: u,
Logger: log.New(log.DebugLevel),
}
defer ts.Close()
got, err := c.Num(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("%q. Client.Num() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if got != len(tt.want) {
t.Errorf("%q. Client.Num() = %v, want %v", tt.name, got, len(tt.want))
}
}
}
func TestClient_All(t *testing.T) {
type args struct {
ctx context.Context

View File

@ -15,6 +15,7 @@ type UsersStore struct {
DeleteF func(context.Context, *chronograf.User) error
GetF func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error)
UpdateF func(context.Context, *chronograf.User) error
NumF func(context.Context) (int, error)
}
// All lists all users from the UsersStore
@ -22,6 +23,11 @@ func (s *UsersStore) All(ctx context.Context) ([]chronograf.User, error) {
return s.AllF(ctx)
}
// Num returns the number of users in the UsersStore
func (s *UsersStore) Num(ctx context.Context) (int, error) {
return s.NumF(ctx)
}
// Add a new User in the UsersStore
func (s *UsersStore) Add(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
return s.AddF(ctx, u)

View File

@ -31,3 +31,7 @@ func (s *UsersStore) Get(ctx context.Context, q chronograf.UserQuery) (*chronogr
func (s *UsersStore) Update(context.Context, *chronograf.User) error {
return fmt.Errorf("failed to update user")
}
func (s *UsersStore) Num(context.Context) (int, error) {
return 0, fmt.Errorf("failed to get number of users")
}

View File

@ -241,3 +241,20 @@ func (s *UsersStore) All(ctx context.Context) ([]chronograf.User, error) {
return us, nil
}
// Num returns the number of users in the UsersStore
// This is unperformant, but should rarely be used.
func (s *UsersStore) Num(ctx context.Context) (int, error) {
err := validOrganization(ctx)
if err != nil {
return 0, err
}
// retrieve all users from the underlying UsersStore
usrs, err := s.All(ctx)
if err != nil {
return 0, err
}
return len(usrs), nil
}

View File

@ -875,3 +875,131 @@ func TestUsersStore_All(t *testing.T) {
}
}
}
func TestUsersStore_Num(t *testing.T) {
type fields struct {
UsersStore chronograf.UsersStore
}
tests := []struct {
name string
fields fields
ctx context.Context
orgID string
want int
wantErr bool
}{
{
name: "No users",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
Name: "howdy",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1338",
Name: "viewer",
},
{
Organization: "1336",
Name: "viewer",
},
},
},
{
Name: "doody2",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1337",
Name: "editor",
},
},
},
{
Name: "doody",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1338",
Name: "editor",
},
},
},
}, nil
},
},
},
ctx: context.Background(),
orgID: "2330",
},
{
name: "get all users",
orgID: "1338",
fields: fields{
UsersStore: &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
Name: "howdy",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1338",
Name: "viewer",
},
{
Organization: "1336",
Name: "viewer",
},
},
},
{
Name: "doody2",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1337",
Name: "editor",
},
},
},
{
Name: "doody",
Provider: "github",
Scheme: "oauth2",
Roles: []chronograf.Role{
{
Organization: "1338",
Name: "editor",
},
},
},
}, nil
},
},
},
ctx: context.Background(),
want: 2,
},
}
for _, tt := range tests {
tt.ctx = context.WithValue(tt.ctx, organizations.ContextKey, tt.orgID)
s := organizations.NewUsersStore(tt.fields.UsersStore, tt.orgID)
got, err := s.Num(tt.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("%q. UsersStore.Num() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
}
if got != tt.want {
t.Errorf("%q. UsersStore.Num() = %d. want %d", tt.name, got, tt.want)
}
}
}