339 lines
7.1 KiB
Go
339 lines
7.1 KiB
Go
package bolt
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"sync"
|
|
|
|
bolt "github.com/coreos/bbolt"
|
|
"github.com/influxdata/platform"
|
|
)
|
|
|
|
var (
|
|
viewBucket = []byte("viewsv2")
|
|
)
|
|
|
|
func (c *Client) initializeViews(ctx context.Context, tx *bolt.Tx) error {
|
|
if _, err := tx.CreateBucketIfNotExists([]byte(viewBucket)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FindViewByID retrieves a view by id.
|
|
func (c *Client) FindViewByID(ctx context.Context, id platform.ID) (*platform.View, error) {
|
|
var d *platform.View
|
|
|
|
err := c.db.View(func(tx *bolt.Tx) error {
|
|
dash, err := c.findViewByID(ctx, tx, id)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: getOp(platform.OpFindViewByID),
|
|
}
|
|
}
|
|
d = dash
|
|
return nil
|
|
})
|
|
|
|
return d, err
|
|
}
|
|
|
|
func (c *Client) findViewByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.View, *platform.Error) {
|
|
var d platform.View
|
|
|
|
encodedID, err := id.Encode()
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
v := tx.Bucket(viewBucket).Get(encodedID)
|
|
if len(v) == 0 {
|
|
return nil, &platform.Error{
|
|
Code: platform.ENotFound,
|
|
Msg: platform.ErrViewNotFound,
|
|
}
|
|
}
|
|
|
|
if err := json.Unmarshal(v, &d); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
return &d, nil
|
|
}
|
|
|
|
func (c *Client) copyView(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.View, *platform.Error) {
|
|
v, pe := c.findViewByID(ctx, tx, id)
|
|
if pe != nil {
|
|
return nil, pe
|
|
}
|
|
|
|
view := &platform.View{
|
|
ViewContents: platform.ViewContents{
|
|
Name: v.Name,
|
|
},
|
|
Properties: v.Properties,
|
|
}
|
|
|
|
if pe := c.createView(ctx, tx, view); pe != nil {
|
|
return nil, pe
|
|
}
|
|
|
|
return view, nil
|
|
}
|
|
|
|
// FindView retrieves a view using an arbitrary view filter.
|
|
func (c *Client) FindView(ctx context.Context, filter platform.ViewFilter) (*platform.View, error) {
|
|
if filter.ID != nil {
|
|
return c.FindViewByID(ctx, *filter.ID)
|
|
}
|
|
|
|
var d *platform.View
|
|
err := c.db.View(func(tx *bolt.Tx) error {
|
|
filterFn := filterViewsFn(filter)
|
|
return c.forEachView(ctx, tx, func(dash *platform.View) bool {
|
|
if filterFn(dash) {
|
|
d = dash
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
if d == nil {
|
|
return nil, &platform.Error{
|
|
Code: platform.ENotFound,
|
|
Msg: platform.ErrViewNotFound,
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
func filterViewsFn(filter platform.ViewFilter) func(v *platform.View) bool {
|
|
if filter.ID != nil {
|
|
return func(v *platform.View) bool {
|
|
return v.ID == *filter.ID
|
|
}
|
|
}
|
|
|
|
if len(filter.Types) > 0 {
|
|
var sm sync.Map
|
|
for _, t := range filter.Types {
|
|
sm.Store(t, true)
|
|
}
|
|
return func(v *platform.View) bool {
|
|
_, ok := sm.Load(v.Properties.GetType())
|
|
return ok
|
|
}
|
|
}
|
|
|
|
return func(v *platform.View) bool { return true }
|
|
}
|
|
|
|
// FindViews retrives all views that match an arbitrary view filter.
|
|
func (c *Client) FindViews(ctx context.Context, filter platform.ViewFilter) ([]*platform.View, int, error) {
|
|
ds := []*platform.View{}
|
|
op := getOp(platform.OpFindViews)
|
|
if filter.ID != nil {
|
|
d, err := c.FindViewByID(ctx, *filter.ID)
|
|
if err != nil && platform.ErrorCode(err) != platform.ENotFound {
|
|
return nil, 0, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
if d != nil {
|
|
ds = append(ds, d)
|
|
}
|
|
|
|
return ds, 1, nil
|
|
}
|
|
|
|
err := c.db.View(func(tx *bolt.Tx) error {
|
|
dashs, err := c.findViews(ctx, tx, filter)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
ds = dashs
|
|
return nil
|
|
})
|
|
|
|
return ds, len(ds), err
|
|
}
|
|
|
|
func (c *Client) findViews(ctx context.Context, tx *bolt.Tx, filter platform.ViewFilter) ([]*platform.View, error) {
|
|
ds := []*platform.View{}
|
|
|
|
filterFn := filterViewsFn(filter)
|
|
err := c.forEachView(ctx, tx, func(d *platform.View) bool {
|
|
if filterFn(d) {
|
|
ds = append(ds, d)
|
|
}
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ds, nil
|
|
}
|
|
|
|
// CreateView creates a platform view and sets d.ID.
|
|
func (c *Client) CreateView(ctx context.Context, d *platform.View) error {
|
|
return c.db.Update(func(tx *bolt.Tx) error {
|
|
if pe := c.createView(ctx, tx, d); pe != nil {
|
|
return &platform.Error{
|
|
Op: getOp(platform.OpCreateView),
|
|
Err: pe,
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (c *Client) createView(ctx context.Context, tx *bolt.Tx, d *platform.View) *platform.Error {
|
|
d.ID = c.IDGenerator.ID()
|
|
return c.putView(ctx, tx, d)
|
|
}
|
|
|
|
// PutView will put a view without setting an ID.
|
|
func (c *Client) PutView(ctx context.Context, d *platform.View) error {
|
|
return c.db.Update(func(tx *bolt.Tx) error {
|
|
if pe := c.putView(ctx, tx, d); pe != nil {
|
|
return pe
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (c *Client) putView(ctx context.Context, tx *bolt.Tx, d *platform.View) *platform.Error {
|
|
v, err := json.Marshal(d)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
encodedID, err := d.ID.Encode()
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
if err := tx.Bucket(viewBucket).Put(encodedID, v); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// forEachView will iterate through all views while fn returns true.
|
|
func (c *Client) forEachView(ctx context.Context, tx *bolt.Tx, fn func(*platform.View) bool) error {
|
|
cur := tx.Bucket(viewBucket).Cursor()
|
|
for k, v := cur.First(); k != nil; k, v = cur.Next() {
|
|
d := &platform.View{}
|
|
if err := json.Unmarshal(v, d); err != nil {
|
|
return err
|
|
}
|
|
if !fn(d) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateView updates a view according the parameters set on upd.
|
|
func (c *Client) UpdateView(ctx context.Context, id platform.ID, upd platform.ViewUpdate) (*platform.View, error) {
|
|
var d *platform.View
|
|
err := c.db.Update(func(tx *bolt.Tx) error {
|
|
dash, pe := c.updateView(ctx, tx, id, upd)
|
|
if pe != nil {
|
|
return &platform.Error{
|
|
Err: pe,
|
|
Op: getOp(platform.OpUpdateView),
|
|
}
|
|
}
|
|
d = dash
|
|
return nil
|
|
})
|
|
|
|
return d, err
|
|
}
|
|
|
|
func (c *Client) updateView(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.ViewUpdate) (*platform.View, error) {
|
|
d, err := c.findViewByID(ctx, tx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if upd.Name != nil {
|
|
d.Name = *upd.Name
|
|
}
|
|
|
|
if upd.Properties != nil {
|
|
d.Properties = upd.Properties
|
|
}
|
|
|
|
if err := c.putView(ctx, tx, d); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// DeleteView deletes a view and prunes it from the index.
|
|
func (c *Client) DeleteView(ctx context.Context, id platform.ID) error {
|
|
return c.db.Update(func(tx *bolt.Tx) error {
|
|
if pe := c.deleteView(ctx, tx, id); pe != nil {
|
|
return &platform.Error{
|
|
Err: pe,
|
|
Op: getOp(platform.OpDeleteView),
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (c *Client) deleteView(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error {
|
|
_, pe := c.findViewByID(ctx, tx, id)
|
|
if pe != nil {
|
|
return pe
|
|
}
|
|
encodedID, err := id.Encode()
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
if err := tx.Bucket(viewBucket).Delete(encodedID); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
if err := c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
|
|
ResourceID: id,
|
|
ResourceType: platform.ViewResourceType,
|
|
}); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
return nil
|
|
}
|