feat(bolt): add boltdb implementation of dashboard service
parent
1c8ddc44a9
commit
d14b1905cd
|
@ -0,0 +1,246 @@
|
|||
package bolt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
platform "github.com/influxdata/chronograf/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
dashboardV2Bucket = []byte("dashboardsv3")
|
||||
)
|
||||
|
||||
var _ platform.DashboardService = (*Client)(nil)
|
||||
|
||||
func (c *Client) initializeDashboards(ctx context.Context, tx *bolt.Tx) error {
|
||||
if _, err := tx.CreateBucketIfNotExists([]byte(dashboardV2Bucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindDashboardByID retrieves a dashboard by id.
|
||||
func (c *Client) FindDashboardByID(ctx context.Context, id platform.ID) (*platform.Dashboard, error) {
|
||||
var d *platform.Dashboard
|
||||
|
||||
err := c.db.View(func(tx *bolt.Tx) error {
|
||||
dash, err := c.findDashboardByID(ctx, tx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d = dash
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (c *Client) findDashboardByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Dashboard, error) {
|
||||
var d platform.Dashboard
|
||||
|
||||
v := tx.Bucket(dashboardV2Bucket).Get([]byte(id))
|
||||
|
||||
if len(v) == 0 {
|
||||
return nil, platform.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(v, &d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// FindDashboard retrieves a dashboard using an arbitrary dashboard filter.
|
||||
func (c *Client) FindDashboard(ctx context.Context, filter platform.DashboardFilter) (*platform.Dashboard, error) {
|
||||
if filter.ID != nil {
|
||||
return c.FindDashboardByID(ctx, *filter.ID)
|
||||
}
|
||||
|
||||
var d *platform.Dashboard
|
||||
err := c.db.View(func(tx *bolt.Tx) error {
|
||||
filterFn := filterDashboardsFn(filter)
|
||||
return c.forEachDashboard(ctx, tx, func(dash *platform.Dashboard) bool {
|
||||
if filterFn(dash) {
|
||||
d = dash
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d == nil {
|
||||
return nil, platform.ErrDashboardNotFound
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func filterDashboardsFn(filter platform.DashboardFilter) func(d *platform.Dashboard) bool {
|
||||
if filter.ID != nil {
|
||||
return func(d *platform.Dashboard) bool {
|
||||
return d.ID == *filter.ID
|
||||
}
|
||||
}
|
||||
|
||||
return func(d *platform.Dashboard) bool { return true }
|
||||
}
|
||||
|
||||
// FindDashboards retrives all dashboards that match an arbitrary dashboard filter.
|
||||
func (c *Client) FindDashboards(ctx context.Context, filter platform.DashboardFilter) ([]*platform.Dashboard, int, error) {
|
||||
if filter.ID != nil {
|
||||
d, err := c.FindDashboardByID(ctx, *filter.ID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return []*platform.Dashboard{d}, 1, nil
|
||||
}
|
||||
|
||||
ds := []*platform.Dashboard{}
|
||||
err := c.db.View(func(tx *bolt.Tx) error {
|
||||
dashs, err := c.findDashboards(ctx, tx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ds = dashs
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return ds, len(ds), nil
|
||||
}
|
||||
|
||||
func (c *Client) findDashboards(ctx context.Context, tx *bolt.Tx, filter platform.DashboardFilter) ([]*platform.Dashboard, error) {
|
||||
ds := []*platform.Dashboard{}
|
||||
|
||||
filterFn := filterDashboardsFn(filter)
|
||||
err := c.forEachDashboard(ctx, tx, func(d *platform.Dashboard) bool {
|
||||
if filterFn(d) {
|
||||
ds = append(ds, d)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
// CreateDashboard creates a platform dashboard and sets d.ID.
|
||||
func (c *Client) CreateDashboard(ctx context.Context, d *platform.Dashboard) error {
|
||||
return c.db.Update(func(tx *bolt.Tx) error {
|
||||
id, err := tx.Bucket(dashboardV2Bucket).NextSequence()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.ID = platform.ID(strconv.Itoa(int(id)))
|
||||
|
||||
return c.putDashboard(ctx, tx, d)
|
||||
})
|
||||
}
|
||||
|
||||
// PutDashboard will put a dashboard without setting an ID.
|
||||
func (c *Client) PutDashboard(ctx context.Context, d *platform.Dashboard) error {
|
||||
return c.db.Update(func(tx *bolt.Tx) error {
|
||||
return c.putDashboard(ctx, tx, d)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) putDashboard(ctx context.Context, tx *bolt.Tx, d *platform.Dashboard) error {
|
||||
v, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Bucket(dashboardV2Bucket).Put([]byte(d.ID), v); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// forEachDashboard will iterate through all dashboards while fn returns true.
|
||||
func (c *Client) forEachDashboard(ctx context.Context, tx *bolt.Tx, fn func(*platform.Dashboard) bool) error {
|
||||
cur := tx.Bucket(dashboardV2Bucket).Cursor()
|
||||
for k, v := cur.First(); k != nil; k, v = cur.Next() {
|
||||
d := &platform.Dashboard{}
|
||||
if err := json.Unmarshal(v, d); err != nil {
|
||||
return err
|
||||
}
|
||||
if !fn(d) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateDashboard updates a dashboard according the parameters set on upd.
|
||||
func (c *Client) UpdateDashboard(ctx context.Context, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) {
|
||||
var d *platform.Dashboard
|
||||
err := c.db.Update(func(tx *bolt.Tx) error {
|
||||
dash, err := c.updateDashboard(ctx, tx, id, upd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d = dash
|
||||
return nil
|
||||
})
|
||||
|
||||
return d, err
|
||||
}
|
||||
|
||||
func (c *Client) updateDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) {
|
||||
d, err := c.findDashboardByID(ctx, tx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if upd.Name != nil {
|
||||
d.Name = *upd.Name
|
||||
}
|
||||
|
||||
if upd.Cells != nil {
|
||||
d.Cells = upd.Cells
|
||||
}
|
||||
|
||||
if upd.Templates != nil {
|
||||
d.Templates = upd.Templates
|
||||
}
|
||||
|
||||
if err := c.putDashboard(ctx, tx, d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// DeleteDashboard deletes a dashboard and prunes it from the index.
|
||||
func (c *Client) DeleteDashboard(ctx context.Context, id platform.ID) error {
|
||||
return c.db.Update(func(tx *bolt.Tx) error {
|
||||
return c.deleteDashboard(ctx, tx, id)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) deleteDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID) error {
|
||||
_, err := c.findDashboardByID(ctx, tx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Bucket(dashboardV2Bucket).Delete([]byte(id))
|
||||
}
|
Loading…
Reference in New Issue