feat(bolt): user-resource mapping boltdb implementation (#900)

* add bolt tests for user resource mapping

* add function boilerplate

* add more segfaults

* add create fn for bolt mapping

* initialize user resource mapping bucket

* fix error message

* add find user mapping fn

* fix error message

* implement user resource mapping filter fn

* add rest of user resource mapping filter fn
pull/10616/head
Jade McGough 2018-09-27 12:15:26 -07:00 committed by GitHub
parent 1573ff6c24
commit 232d87b9ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 246 additions and 0 deletions

View File

@ -118,6 +118,11 @@ func (c *Client) initialize(ctx context.Context) error {
return err
}
// Always create UserResourceMapping bucket.
if err := c.initializeUserResourceMappings(ctx, tx); err != nil {
return err
}
return nil
}); err != nil {
return err

View File

@ -0,0 +1,192 @@
package bolt
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/coreos/bbolt"
"github.com/influxdata/platform"
)
var (
userResourceMappingBucket = []byte("userresourcemappingsv1")
)
func (c *Client) initializeUserResourceMappings(ctx context.Context, tx *bolt.Tx) error {
if _, err := tx.CreateBucketIfNotExists([]byte(userResourceMappingBucket)); err != nil {
return err
}
return nil
}
func filterMappingsFn(filter platform.UserResourceMappingFilter) func(m *platform.UserResourceMapping) bool {
if filter.UserID != nil && filter.ResourceID != nil && filter.UserType != "" {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.ResourceID, filter.ResourceID) &&
bytes.Equal(m.UserID, filter.UserID) &&
m.UserType == filter.UserType
}
}
if filter.UserID != nil && filter.ResourceID != nil {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.UserID, filter.UserID) &&
bytes.Equal(m.ResourceID, filter.ResourceID)
}
}
if filter.UserID != nil && filter.UserType != "" {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.UserID, filter.UserID) &&
m.UserType == filter.UserType
}
}
if filter.ResourceID != nil && filter.UserType != "" {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.ResourceID, filter.ResourceID) &&
m.UserType == filter.UserType
}
}
if filter.UserID != nil {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.UserID, filter.UserID)
}
}
if filter.ResourceID != nil {
return func(m *platform.UserResourceMapping) bool {
return bytes.Equal(m.ResourceID, filter.ResourceID)
}
}
if filter.UserType != "" {
return func(m *platform.UserResourceMapping) bool {
return m.UserType == filter.UserType
}
}
return func(m *platform.UserResourceMapping) bool { return true }
}
func (c *Client) FindUserResourceMappings(ctx context.Context, filter platform.UserResourceMappingFilter, opt ...platform.FindOptions) ([]*platform.UserResourceMapping, int, error) {
ms := []*platform.UserResourceMapping{}
err := c.db.View(func(tx *bolt.Tx) error {
mappings, err := c.findUserResourceMappings(ctx, tx, filter)
if err != nil {
return err
}
ms = mappings
return nil
})
if err != nil {
return nil, 0, err
}
return ms, len(ms), nil
}
func (c *Client) findUserResourceMappings(ctx context.Context, tx *bolt.Tx, filter platform.UserResourceMappingFilter) ([]*platform.UserResourceMapping, error) {
ms := []*platform.UserResourceMapping{}
filterFn := filterMappingsFn(filter)
err := c.forEachUserResourceMapping(ctx, tx, func(m *platform.UserResourceMapping) bool {
if filterFn(m) {
ms = append(ms, m)
}
return true
})
if err != nil {
return nil, err
}
return ms, nil
}
func (c *Client) findUserResourceMapping(ctx context.Context, tx *bolt.Tx, resourceID platform.ID, userID platform.ID) (*platform.UserResourceMapping, error) {
var m platform.UserResourceMapping
key := userResourceKey(&platform.UserResourceMapping{
ResourceID: resourceID,
UserID: userID,
})
v := tx.Bucket(userResourceMappingBucket).Get(key)
if len(v) == 0 {
return nil, fmt.Errorf("userResource mapping not found")
}
if err := json.Unmarshal(v, &m); err != nil {
return nil, err
}
return &m, nil
}
func (c *Client) CreateUserResourceMapping(ctx context.Context, m *platform.UserResourceMapping) error {
return c.db.Update(func(tx *bolt.Tx) error {
unique := c.uniqueUserResourceMapping(ctx, tx, m)
if !unique {
return fmt.Errorf("mapping for user %s already exists", m.UserID.String())
}
v, err := json.Marshal(m)
if err != nil {
return err
}
if err := tx.Bucket(userResourceMappingBucket).Put(userResourceKey(m), v); err != nil {
return err
}
return nil
})
}
func userResourceKey(m *platform.UserResourceMapping) []byte {
k := make([]byte, len(m.ResourceID)+len(m.UserID))
copy(k, m.ResourceID)
copy(k[len(m.ResourceID):], []byte(m.UserID))
return k
}
func (c *Client) forEachUserResourceMapping(ctx context.Context, tx *bolt.Tx, fn func(*platform.UserResourceMapping) bool) error {
cur := tx.Bucket(userResourceMappingBucket).Cursor()
for k, v := cur.First(); k != nil; k, v = cur.Next() {
m := &platform.UserResourceMapping{}
if err := json.Unmarshal(v, m); err != nil {
return err
}
if !fn(m) {
break
}
}
return nil
}
func (c *Client) uniqueUserResourceMapping(ctx context.Context, tx *bolt.Tx, m *platform.UserResourceMapping) bool {
v := tx.Bucket(userResourceMappingBucket).Get(userResourceKey(m))
return len(v) == 0
}
func (c *Client) DeleteUserResourceMapping(ctx context.Context, resourceID platform.ID, userID platform.ID) error {
return c.db.Update(func(tx *bolt.Tx) error {
return c.deleteUserResourceMapping(ctx, tx, resourceID, userID)
})
}
func (c *Client) deleteUserResourceMapping(ctx context.Context, tx *bolt.Tx, resourceID platform.ID, userID platform.ID) error {
m, err := c.findUserResourceMapping(ctx, tx, resourceID, userID)
if err != nil {
return err
}
return tx.Bucket(userResourceMappingBucket).Delete(userResourceKey(m))
}

View File

@ -0,0 +1,43 @@
package bolt_test
import (
"context"
"testing"
"github.com/influxdata/platform"
platformtesting "github.com/influxdata/platform/testing"
)
func initUserResourceMappingService(f platformtesting.UserResourceFields, t *testing.T) (platform.UserResourceMappingService, func()) {
c, closeFn, err := NewTestClient()
if err != nil {
t.Fatalf("failed to create new bolt client: %v", err)
}
ctx := context.Background()
for _, m := range f.UserResourceMappings {
if err := c.CreateUserResourceMapping(ctx, m); err != nil {
t.Fatalf("failed to populate mappings")
}
}
return c, func() {
defer closeFn()
for _, m := range f.UserResourceMappings {
if err := c.DeleteUserResourceMapping(ctx, m.ResourceID, m.UserID); err != nil {
t.Logf("failed to remove user resource mapping: %v", err)
}
}
}
}
func TestUserResourceMappingService_FindUserResourceMappings(t *testing.T) {
platformtesting.FindUserResourceMappings(initUserResourceMappingService, t)
}
func TestUserResourceMappingService_CreateUserResourceMapping(t *testing.T) {
platformtesting.CreateUserResourceMapping(initUserResourceMappingService, t)
}
func TestUserResourceMappingService_DeleteUserResourceMapping(t *testing.T) {
platformtesting.DeleteUserResourceMapping(initUserResourceMappingService, t)
}

View File

@ -164,6 +164,11 @@ func platformF(cmd *cobra.Command, args []string) {
userSvc = c
}
var userResourceSvc platform.UserResourceMappingService
{
userResourceSvc = c
}
var dashboardSvc platform.DashboardService
{
dashboardSvc = c
@ -277,6 +282,7 @@ func platformF(cmd *cobra.Command, args []string) {
orgHandler := http.NewOrgHandler()
orgHandler.OrganizationService = orgSvc
orgHandler.BucketService = bucketSvc
orgHandler.UserResourceMappingService = userResourceSvc
userHandler := http.NewUserHandler()
userHandler.UserService = userSvc