495 lines
11 KiB
Go
495 lines
11 KiB
Go
package inmem
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
platform "github.com/influxdata/influxdb"
|
|
)
|
|
|
|
func (s *Service) loadDashboard(ctx context.Context, id platform.ID) (*platform.Dashboard, *platform.Error) {
|
|
i, ok := s.dashboardKV.Load(id.String())
|
|
if !ok {
|
|
return nil, &platform.Error{
|
|
Code: platform.ENotFound,
|
|
Msg: platform.ErrDashboardNotFound,
|
|
}
|
|
}
|
|
|
|
d, ok := i.(*platform.Dashboard)
|
|
if !ok {
|
|
return nil, &platform.Error{
|
|
Code: platform.EInvalid,
|
|
Msg: fmt.Sprintf("type %T is not a dashboard", i),
|
|
}
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// FindDashboardByID returns a single dashboard by ID.
|
|
func (s *Service) FindDashboardByID(ctx context.Context, id platform.ID) (*platform.Dashboard, error) {
|
|
d, pe := s.loadDashboard(ctx, id)
|
|
if pe != nil {
|
|
return nil, &platform.Error{
|
|
Op: OpPrefix + platform.OpFindDashboardByID,
|
|
Err: pe,
|
|
}
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
func filterDashboardFn(filter platform.DashboardFilter) func(d *platform.Dashboard) bool {
|
|
if filter.OrganizationID != nil {
|
|
return func(d *platform.Dashboard) bool {
|
|
return d.OrganizationID == *filter.OrganizationID
|
|
}
|
|
}
|
|
|
|
if len(filter.IDs) > 0 {
|
|
m := map[platform.ID]struct{}{}
|
|
for _, id := range filter.IDs {
|
|
if id != nil {
|
|
m[*id] = struct{}{}
|
|
}
|
|
}
|
|
return func(d *platform.Dashboard) bool {
|
|
_, ok := m[d.ID]
|
|
return ok
|
|
}
|
|
}
|
|
|
|
return func(d *platform.Dashboard) bool { return true }
|
|
}
|
|
|
|
func (s *Service) forEachDashboard(ctx context.Context, opts platform.FindOptions, fn func(d *platform.Dashboard) bool) error {
|
|
var err error
|
|
ds := make([]*platform.Dashboard, 0)
|
|
s.dashboardKV.Range(func(k, v interface{}) bool {
|
|
d, ok := v.(*platform.Dashboard)
|
|
if !ok {
|
|
err = fmt.Errorf("type %T is not a dashboard", v)
|
|
return false
|
|
}
|
|
ds = append(ds, d)
|
|
|
|
return true
|
|
})
|
|
|
|
platform.SortDashboards(opts, ds)
|
|
|
|
for _, d := range ds {
|
|
if !fn(d) {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// FindDashboards implements platform.DashboardService interface.
|
|
func (s *Service) FindDashboards(ctx context.Context, filter platform.DashboardFilter, opts platform.FindOptions) ([]*platform.Dashboard, int, error) {
|
|
var ds []*platform.Dashboard
|
|
op := OpPrefix + platform.OpFindDashboards
|
|
if len(filter.IDs) == 1 {
|
|
d, err := s.FindDashboardByID(ctx, *filter.IDs[0])
|
|
if err != nil && platform.ErrorCode(err) != platform.ENotFound {
|
|
return ds, 0, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
if d == nil {
|
|
return ds, 0, nil
|
|
}
|
|
return []*platform.Dashboard{d}, 1, nil
|
|
}
|
|
|
|
var count int
|
|
filterFn := filterDashboardFn(filter)
|
|
err := s.forEachDashboard(ctx, opts, func(d *platform.Dashboard) bool {
|
|
if filterFn(d) {
|
|
if count >= opts.Offset {
|
|
ds = append(ds, d)
|
|
}
|
|
count++
|
|
}
|
|
if opts.Limit > 0 && len(ds) >= opts.Limit {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return ds, len(ds), err
|
|
}
|
|
|
|
// CreateDashboard implements platform.DashboardService interface.
|
|
func (s *Service) CreateDashboard(ctx context.Context, d *platform.Dashboard) error {
|
|
d.ID = s.IDGenerator.ID()
|
|
d.Meta.CreatedAt = s.time()
|
|
err := s.PutDashboardWithMeta(ctx, d)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: platform.OpCreateDashboard,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PutDashboard implements platform.DashboardService interface.
|
|
func (s *Service) PutDashboard(ctx context.Context, d *platform.Dashboard) error {
|
|
for _, cell := range d.Cells {
|
|
if err := s.PutCellView(ctx, cell); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
s.dashboardKV.Store(d.ID.String(), d)
|
|
return nil
|
|
}
|
|
|
|
// PutCellView puts the view for a cell.
|
|
func (s *Service) PutCellView(ctx context.Context, cell *platform.Cell) error {
|
|
v := &platform.View{}
|
|
v.ID = cell.ID
|
|
return s.PutView(ctx, v)
|
|
}
|
|
|
|
// PutDashboardWithMeta sets a dashboard while updating the meta field of a dashboard.
|
|
func (s *Service) PutDashboardWithMeta(ctx context.Context, d *platform.Dashboard) error {
|
|
d.Meta.UpdatedAt = s.time()
|
|
return s.PutDashboard(ctx, d)
|
|
}
|
|
|
|
// UpdateDashboard implements platform.DashboardService interface.
|
|
func (s *Service) UpdateDashboard(ctx context.Context, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) {
|
|
op := OpPrefix + platform.OpUpdateDashboard
|
|
if err := upd.Valid(); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
d, err := s.FindDashboardByID(ctx, id)
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
if err := upd.Apply(d); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
if err := s.PutDashboardWithMeta(ctx, d); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// DeleteDashboard implements platform.DashboardService interface.
|
|
func (s *Service) DeleteDashboard(ctx context.Context, id platform.ID) error {
|
|
op := OpPrefix + platform.OpDeleteDashboard
|
|
if _, err := s.FindDashboardByID(ctx, id); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
s.dashboardKV.Delete(id.String())
|
|
// err := s.deleteLabel(ctx, platform.LabelFilter{ResourceID: id})
|
|
// if err != nil {
|
|
// return &platform.Error{
|
|
// Err: err,
|
|
// Op: op,
|
|
// }
|
|
// }
|
|
return nil
|
|
}
|
|
|
|
// AddDashboardCell adds a new cell to the dashboard.
|
|
func (s *Service) AddDashboardCell(ctx context.Context, id platform.ID, cell *platform.Cell, opts platform.AddDashboardCellOptions) error {
|
|
op := OpPrefix + platform.OpAddDashboardCell
|
|
d, err := s.FindDashboardByID(ctx, id)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
cell.ID = s.IDGenerator.ID()
|
|
if err := s.createCellView(ctx, cell); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
d.Cells = append(d.Cells, cell)
|
|
if err = s.PutDashboardWithMeta(ctx, d); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) createCellView(ctx context.Context, cell *platform.Cell) *platform.Error {
|
|
// If not view exists create the view
|
|
view := &platform.View{}
|
|
view.ID = cell.ID
|
|
if err := s.PutView(ctx, view); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PutDashboardCell replaces a dashboad cell with the cell contents.
|
|
func (s *Service) PutDashboardCell(ctx context.Context, id platform.ID, cell *platform.Cell) error {
|
|
d, err := s.FindDashboardByID(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
view := &platform.View{}
|
|
view.ID = cell.ID
|
|
if err := s.PutView(ctx, view); err != nil {
|
|
return err
|
|
}
|
|
|
|
d.Cells = append(d.Cells, cell)
|
|
return s.PutDashboard(ctx, d)
|
|
}
|
|
|
|
// RemoveDashboardCell removes a dashboard cell from the dashboard.
|
|
func (s *Service) RemoveDashboardCell(ctx context.Context, dashboardID platform.ID, cellID platform.ID) error {
|
|
op := OpPrefix + platform.OpRemoveDashboardCell
|
|
d, err := s.FindDashboardByID(ctx, dashboardID)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
idx := -1
|
|
for i, cell := range d.Cells {
|
|
if cell.ID == cellID {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
if idx == -1 {
|
|
return &platform.Error{
|
|
Code: platform.ENotFound,
|
|
Op: op,
|
|
Msg: platform.ErrCellNotFound,
|
|
}
|
|
}
|
|
|
|
if err := s.DeleteView(ctx, d.Cells[idx].ID); err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
d.Cells = append(d.Cells[:idx], d.Cells[idx+1:]...)
|
|
return s.PutDashboardWithMeta(ctx, d)
|
|
|
|
}
|
|
|
|
// UpdateDashboardCell will remove a cell from a dashboard.
|
|
func (s *Service) UpdateDashboardCell(ctx context.Context, dashboardID platform.ID, cellID platform.ID, upd platform.CellUpdate) (*platform.Cell, error) {
|
|
op := OpPrefix + platform.OpUpdateDashboardCell
|
|
if err := upd.Valid(); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
d, err := s.FindDashboardByID(ctx, dashboardID)
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
idx := -1
|
|
for i, cell := range d.Cells {
|
|
if cell.ID == cellID {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
if idx == -1 {
|
|
return nil, &platform.Error{
|
|
Msg: platform.ErrCellNotFound,
|
|
Op: op,
|
|
Code: platform.ENotFound,
|
|
}
|
|
}
|
|
|
|
if err := upd.Apply(d.Cells[idx]); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
cell := d.Cells[idx]
|
|
|
|
if err := s.PutDashboardWithMeta(ctx, d); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
return cell, nil
|
|
}
|
|
|
|
// ReplaceDashboardCells replaces many dashboard cells.
|
|
func (s *Service) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs []*platform.Cell) error {
|
|
op := OpPrefix + platform.OpReplaceDashboardCells
|
|
d, err := s.FindDashboardByID(ctx, id)
|
|
if err != nil {
|
|
return &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
ids := map[string]*platform.Cell{}
|
|
for _, cell := range d.Cells {
|
|
ids[cell.ID.String()] = cell
|
|
}
|
|
|
|
for _, cell := range cs {
|
|
if !cell.ID.Valid() {
|
|
return &platform.Error{
|
|
Code: platform.EInvalid,
|
|
Op: op,
|
|
Msg: "cannot provide empty cell id",
|
|
}
|
|
}
|
|
|
|
if _, ok := ids[cell.ID.String()]; !ok {
|
|
return &platform.Error{
|
|
Code: platform.EConflict,
|
|
Op: op,
|
|
Msg: "cannot replace cells that were not already present",
|
|
}
|
|
}
|
|
}
|
|
|
|
d.Cells = cs
|
|
|
|
return s.PutDashboardWithMeta(ctx, d)
|
|
}
|
|
|
|
// GetDashboardCellView retrieves the view for a dashboard cell.
|
|
func (s *Service) GetDashboardCellView(ctx context.Context, dashboardID, cellID platform.ID) (*platform.View, error) {
|
|
v, err := s.FindViewByID(ctx, cellID)
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: OpPrefix + platform.OpGetDashboardCellView,
|
|
}
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
// UpdateDashboardCellView updates the view for a dashboard cell.
|
|
func (s *Service) UpdateDashboardCellView(ctx context.Context, dashboardID, cellID platform.ID, upd platform.ViewUpdate) (*platform.View, error) {
|
|
op := OpPrefix + platform.OpUpdateDashboardCellView
|
|
|
|
v, err := s.FindViewByID(ctx, cellID)
|
|
if err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
if err := upd.Apply(v); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
if err := s.PutView(ctx, v); err != nil {
|
|
return nil, &platform.Error{
|
|
Err: err,
|
|
Op: op,
|
|
}
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
func (s *Service) loadView(ctx context.Context, id platform.ID) (*platform.View, *platform.Error) {
|
|
i, ok := s.viewKV.Load(id.String())
|
|
if !ok {
|
|
return nil, &platform.Error{
|
|
Code: platform.ENotFound,
|
|
Msg: "view not found",
|
|
}
|
|
}
|
|
|
|
d, ok := i.(*platform.View)
|
|
if !ok {
|
|
return nil, &platform.Error{
|
|
Code: platform.EInvalid,
|
|
Msg: fmt.Sprintf("type %T is not a view", i),
|
|
}
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
// FindViewByID returns a single view by ID.
|
|
func (s *Service) FindViewByID(ctx context.Context, id platform.ID) (*platform.View, error) {
|
|
v, pe := s.loadView(ctx, id)
|
|
if pe != nil {
|
|
return nil, pe
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
// PutView sets view with the current ID.
|
|
func (s *Service) PutView(ctx context.Context, c *platform.View) error {
|
|
if c.Properties == nil {
|
|
c.Properties = platform.EmptyViewProperties{}
|
|
}
|
|
s.viewKV.Store(c.ID.String(), c)
|
|
return nil
|
|
}
|
|
|
|
// DeleteView removes a view by ID.
|
|
func (s *Service) DeleteView(ctx context.Context, id platform.ID) error {
|
|
if _, err := s.FindViewByID(ctx, id); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.viewKV.Delete(id.String())
|
|
return nil
|
|
}
|