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 fnpull/10616/head
parent
1573ff6c24
commit
232d87b9ac
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue