fix(http): convert dashboard errors

pull/10616/head
Kelvin Wang 2018-12-11 13:38:24 -05:00
parent f32b6f2fea
commit 6ca7c68e2e
8 changed files with 615 additions and 303 deletions

View File

@ -3,7 +3,6 @@ package bolt
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
@ -51,26 +50,37 @@ func (c *Client) FindDashboardByID(ctx context.Context, id platform.ID) (*platfo
})
if err != nil {
return nil, err
return nil, &platform.Error{
Op: getOp(platform.OpFindDashboardByID),
Err: err,
}
}
return d, nil
}
func (c *Client) findDashboardByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Dashboard, error) {
func (c *Client) findDashboardByID(ctx context.Context, tx *bolt.Tx, id platform.ID) (*platform.Dashboard, *platform.Error) {
encodedID, err := id.Encode()
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
v := tx.Bucket(dashboardBucket).Get(encodedID)
if len(v) == 0 {
return nil, platform.ErrDashboardNotFound
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
}
var d platform.Dashboard
if err := json.Unmarshal(v, &d); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
}
}
return &d, nil
@ -99,7 +109,10 @@ func (c *Client) FindDashboard(ctx context.Context, filter platform.DashboardFil
}
if d == nil {
return nil, platform.ErrDashboardNotFound
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
}
return d, nil
@ -122,19 +135,23 @@ func filterDashboardsFn(filter platform.DashboardFilter) func(d *platform.Dashbo
// FindDashboards retrives all dashboards that match an arbitrary dashboard filter.
func (c *Client) FindDashboards(ctx context.Context, filter platform.DashboardFilter, opts platform.FindOptions) ([]*platform.Dashboard, int, error) {
ds := []*platform.Dashboard{}
if len(filter.IDs) == 1 {
d, err := c.FindDashboardByID(ctx, *filter.IDs[0])
if err != nil {
return nil, 0, err
if err != nil && platform.ErrorCode(err) != platform.ENotFound {
return ds, 0, &platform.Error{
Err: err,
Op: getOp(platform.OpFindDashboardByID),
}
}
if d == nil {
return ds, 0, nil
}
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 {
if err != nil && platform.ErrorCode(err) != platform.ENotFound {
return err
}
ds = dashs
@ -142,7 +159,10 @@ func (c *Client) FindDashboards(ctx context.Context, filter platform.DashboardFi
})
if err != nil {
return nil, 0, err
return nil, 0, &platform.Error{
Err: err,
Op: getOp(platform.OpFindDashboards),
}
}
platform.SortDashboards(opts.SortBy, ds)
@ -170,7 +190,7 @@ func (c *Client) findDashboards(ctx context.Context, tx *bolt.Tx, filter platfor
// 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 {
err := c.db.Update(func(tx *bolt.Tx) error {
d.ID = c.IDGenerator.ID()
for _, cell := range d.Cells {
@ -190,6 +210,13 @@ func (c *Client) CreateDashboard(ctx context.Context, d *platform.Dashboard) err
return c.putDashboardWithMeta(ctx, tx, d)
})
if err != nil {
return &platform.Error{
Err: err,
Op: getOp(platform.OpCreateDashboard),
}
}
return nil
}
func (c *Client) createViewIfNotExists(ctx context.Context, tx *bolt.Tx, cell *platform.Cell, opts platform.AddDashboardCellOptions) error {
@ -226,7 +253,7 @@ func (c *Client) createViewIfNotExists(ctx context.Context, tx *bolt.Tx, cell *p
// ReplaceDashboardCells updates the positions of each cell in a dashboard concurrently.
func (c *Client) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs []*platform.Cell) error {
return c.db.Update(func(tx *bolt.Tx) error {
err := c.db.Update(func(tx *bolt.Tx) error {
d, err := c.findDashboardByID(ctx, tx, id)
if err != nil {
return err
@ -239,16 +266,25 @@ func (c *Client) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs [
for _, cell := range cs {
if !cell.ID.Valid() {
return fmt.Errorf("cannot provide empty cell id")
return &platform.Error{
Code: platform.EInvalid,
Msg: "cannot provide empty cell id",
}
}
cl, ok := ids[cell.ID.String()]
if !ok {
return fmt.Errorf("cannot replace cells that were not already present")
return &platform.Error{
Code: platform.EConflict,
Msg: "cannot replace cells that were not already present",
}
}
if cl.ViewID != cell.ViewID {
return fmt.Errorf("cannot update view id in replace")
return &platform.Error{
Code: platform.EInvalid,
Msg: "cannot update view id in replace",
}
}
}
@ -259,11 +295,18 @@ func (c *Client) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs [
return c.putDashboardWithMeta(ctx, tx, d)
})
if err != nil {
return &platform.Error{
Op: getOp(platform.OpReplaceDashboardCells),
Err: err,
}
}
return nil
}
// AddDashboardCell adds a cell to a dashboard and sets the cells ID.
func (c *Client) AddDashboardCell(ctx context.Context, id platform.ID, cell *platform.Cell, opts platform.AddDashboardCellOptions) error {
return c.db.Update(func(tx *bolt.Tx) error {
err := c.db.Update(func(tx *bolt.Tx) error {
d, err := c.findDashboardByID(ctx, tx, id)
if err != nil {
return err
@ -281,14 +324,25 @@ func (c *Client) AddDashboardCell(ctx context.Context, id platform.ID, cell *pla
return c.putDashboardWithMeta(ctx, tx, d)
})
if err != nil {
return &platform.Error{
Err: err,
Op: getOp(platform.OpAddDashboardCell),
}
}
return nil
}
// RemoveDashboardCell removes a cell from a dashboard.
func (c *Client) RemoveDashboardCell(ctx context.Context, dashboardID, cellID platform.ID) error {
op := getOp(platform.OpRemoveDashboardCell)
return c.db.Update(func(tx *bolt.Tx) error {
d, err := c.findDashboardByID(ctx, tx, dashboardID)
if err != nil {
return err
return &platform.Error{
Err: err,
Op: op,
}
}
idx := -1
@ -299,27 +353,47 @@ func (c *Client) RemoveDashboardCell(ctx context.Context, dashboardID, cellID pl
}
}
if idx == -1 {
return platform.ErrCellNotFound
return &platform.Error{
Code: platform.ENotFound,
Op: op,
Msg: platform.ErrCellNotFound,
}
}
if err := c.deleteView(ctx, tx, d.Cells[idx].ViewID); err != nil {
return err
return &platform.Error{
Err: err,
Op: op,
}
}
d.Cells = append(d.Cells[:idx], d.Cells[idx+1:]...)
if err := c.appendDashboardEventToLog(ctx, tx, d.ID, dashboardCellRemovedEvent); err != nil {
return err
return &platform.Error{
Err: err,
Op: op,
}
}
return c.putDashboardWithMeta(ctx, tx, d)
if err := c.putDashboardWithMeta(ctx, tx, d); err != nil {
return &platform.Error{
Err: err,
Op: op,
}
}
return nil
})
}
// UpdateDashboardCell udpates a cell on a dashboard.
func (c *Client) UpdateDashboardCell(ctx context.Context, dashboardID, cellID platform.ID, upd platform.CellUpdate) (*platform.Cell, error) {
op := getOp(platform.OpUpdateDashboardCell)
if err := upd.Valid(); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
var cell *platform.Cell
@ -337,7 +411,11 @@ func (c *Client) UpdateDashboardCell(ctx context.Context, dashboardID, cellID pl
}
}
if idx == -1 {
return platform.ErrCellNotFound
return &platform.Error{
Code: platform.ENotFound,
Op: op,
Msg: platform.ErrCellNotFound,
}
}
if err := upd.Apply(d.Cells[idx]); err != nil {
@ -354,7 +432,10 @@ func (c *Client) UpdateDashboardCell(ctx context.Context, dashboardID, cellID pl
})
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
return cell, nil
@ -420,6 +501,12 @@ func (c *Client) UpdateDashboard(ctx context.Context, id platform.ID, upd platfo
return nil
})
if err != nil {
return nil, &platform.Error{
Err: err,
Op: getOp(platform.OpUpdateDashboard),
}
}
return d, err
}
@ -448,38 +535,58 @@ func (c *Client) updateDashboard(ctx context.Context, tx *bolt.Tx, id platform.I
// 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)
if pe := c.deleteDashboard(ctx, tx, id); pe != nil {
return &platform.Error{
Err: pe,
Op: getOp(platform.OpDeleteDashboard),
}
}
return nil
})
}
func (c *Client) deleteDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID) error {
d, err := c.findDashboardByID(ctx, tx, id)
if err != nil {
return err
func (c *Client) deleteDashboard(ctx context.Context, tx *bolt.Tx, id platform.ID) *platform.Error {
d, pe := c.findDashboardByID(ctx, tx, id)
if pe != nil {
return pe
}
for _, cell := range d.Cells {
if err := c.deleteView(ctx, tx, cell.ViewID); err != nil {
return err
return &platform.Error{
Err: err,
}
}
}
encodedID, err := id.Encode()
if err != nil {
return err
return &platform.Error{
Err: err,
}
}
if err := tx.Bucket(dashboardBucket).Delete(encodedID); err != nil {
return err
return &platform.Error{
Err: err,
}
}
err = c.deleteLabels(ctx, tx, platform.LabelFilter{ResourceID: id})
if err != nil {
return err
return &platform.Error{
Err: err,
}
}
// TODO(desa): add DeleteKeyValueLog method and use it here.
return c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
err = c.deleteUserResourceMappings(ctx, tx, platform.UserResourceMappingFilter{
ResourceID: id,
ResourceType: platform.DashboardResourceType,
})
if err != nil {
return &platform.Error{
Err: err,
}
}
return nil
}
const dashboardOperationLogKeyPrefix = "dashboard"

View File

@ -5,10 +5,11 @@ import (
"testing"
"github.com/influxdata/platform"
"github.com/influxdata/platform/bolt"
platformtesting "github.com/influxdata/platform/testing"
)
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, func()) {
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, string, func()) {
c, closeFn, err := NewTestClient()
if err != nil {
t.Fatalf("failed to create new bolt client: %v", err)
@ -26,7 +27,7 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat
t.Fatalf("failed to populate views")
}
}
return c, func() {
return c, bolt.OpPrefix, func() {
defer closeFn()
for _, b := range f.Dashboards {
if err := c.DeleteDashboard(ctx, b.ID); err != nil {
@ -41,38 +42,6 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat
}
}
func TestDashboardService_CreateDashboard(t *testing.T) {
platformtesting.CreateDashboard(initDashboardService, t)
}
func TestDashboardService_FindDashboardByID(t *testing.T) {
platformtesting.FindDashboardByID(initDashboardService, t)
}
func TestDashboardService_FindDashboards(t *testing.T) {
platformtesting.FindDashboards(initDashboardService, t)
}
func TestDashboardService_DeleteDashboard(t *testing.T) {
platformtesting.DeleteDashboard(initDashboardService, t)
}
func TestDashboardService_UpdateDashboard(t *testing.T) {
platformtesting.UpdateDashboard(initDashboardService, t)
}
func TestDashboardService_AddDashboardCell(t *testing.T) {
platformtesting.AddDashboardCell(initDashboardService, t)
}
func TestDashboardService_RemoveDashboardCell(t *testing.T) {
platformtesting.RemoveDashboardCell(initDashboardService, t)
}
func TestDashboardService_UpdateDashboardCell(t *testing.T) {
platformtesting.UpdateDashboardCell(initDashboardService, t)
}
func TestDashboardService_ReplaceDashboardCells(t *testing.T) {
platformtesting.ReplaceDashboardCells(initDashboardService, t)
func TestDashboardService(t *testing.T) {
platformtesting.DashboardService(initDashboardService, t)
}

View File

@ -2,16 +2,28 @@ package platform
import (
"context"
"fmt"
"sort"
"time"
)
// ErrDashboardNotFound is the error for a missing dashboard.
const ErrDashboardNotFound = ChronografError("dashboard not found")
// ErrDashboardNotFound is the error msg for a missing dashboard.
const ErrDashboardNotFound = "dashboard not found"
// ErrCellNotFound is the error for a missing cell.
const ErrCellNotFound = ChronografError("cell not found")
// ErrCellNotFound is the error msg for a missing cell.
const ErrCellNotFound = "cell not found"
// ops for dashboard service.
const (
OpFindDashboardByID = "FindDashboardByID"
OpFindDashboards = "FindDashboards"
OpCreateDashboard = "CreateDashboard"
OpUpdateDashboard = "UpdateDashboard"
OpAddDashboardCell = "AddDashboardCell"
OpRemoveDashboardCell = "RemoveDashboardCell"
OpUpdateDashboardCell = "UpdateDashboardCell"
OpDeleteDashboard = "DeleteDashboard"
OpReplaceDashboardCells = "ReplaceDashboardCells"
)
// DashboardService represents a service for managing dashboard data.
type DashboardService interface {
@ -54,7 +66,7 @@ type Dashboard struct {
Meta DashboardMeta `json:"meta"`
}
// Dashboard meta contains meta information about dashboards
// DashboardMeta contains meta information about dashboards
type DashboardMeta struct {
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
@ -125,9 +137,12 @@ func (u DashboardUpdate) Apply(d *Dashboard) error {
}
// Valid returns an error if the dashboard update is invalid.
func (u DashboardUpdate) Valid() error {
func (u DashboardUpdate) Valid() *Error {
if u.Name == nil && u.Description == nil {
return fmt.Errorf("must update at least one attribute")
return &Error{
Code: EInvalid,
Msg: "must update at least one attribute",
}
}
return nil
@ -175,9 +190,12 @@ func (u CellUpdate) Apply(c *Cell) error {
}
// Valid returns an error if the cell update is invalid.
func (u CellUpdate) Valid() error {
func (u CellUpdate) Valid() *Error {
if u.H == nil && u.W == nil && u.Y == nil && u.X == nil && !u.ViewID.Valid() {
return fmt.Errorf("must update at least one attribute")
return &Error{
Code: EInvalid,
Msg: "must update at least one attribute",
}
}
return nil

View File

@ -88,9 +88,12 @@ type dashboardResponse struct {
}
func (d dashboardResponse) toPlatform() *platform.Dashboard {
cells := make([]*platform.Cell, 0, len(d.Cells))
var cells []*platform.Cell
if len(d.Cells) > 0 {
cells = make([]*platform.Cell, len(d.Cells))
}
for i := range d.Cells {
cells = append(cells, d.Cells[i].toPlatform())
cells[i] = d.Cells[i].toPlatform()
}
return &platform.Dashboard{
ID: d.ID,
@ -221,7 +224,7 @@ func (h *DashboardHandler) handleGetDashboards(w http.ResponseWriter, r *http.Re
dashboards, _, err := h.DashboardService.FindDashboards(ctx, req.filter, req.opts)
if err != nil {
EncodeError(ctx, errors.InternalErrorf("Error loading dashboards: %v", err), w)
EncodeError(ctx, err, w)
return
}
@ -292,7 +295,9 @@ func newGetDashboardsResponse(dashboards []*platform.Dashboard) getDashboardsRes
}
for _, dashboard := range dashboards {
res.Dashboards = append(res.Dashboards, newDashboardResponse(dashboard))
if dashboard != nil {
res.Dashboards = append(res.Dashboards, newDashboardResponse(dashboard))
}
}
return res
@ -344,9 +349,6 @@ func (h *DashboardHandler) handleGetDashboard(w http.ResponseWriter, r *http.Req
dashboard, err := h.DashboardService.FindDashboardByID(ctx, req.DashboardID)
if err != nil {
if err == platform.ErrDashboardNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -454,9 +456,6 @@ func (h *DashboardHandler) handleDeleteDashboard(w http.ResponseWriter, r *http.
}
if err := h.DashboardService.DeleteDashboard(ctx, req.DashboardID); err != nil {
if err == platform.ErrDashboardNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -496,9 +495,6 @@ func (h *DashboardHandler) handlePatchDashboard(w http.ResponseWriter, r *http.R
}
dashboard, err := h.DashboardService.UpdateDashboard(ctx, req.DashboardID, req.Upd)
if err != nil {
if err == platform.ErrDashboardNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -520,7 +516,6 @@ func decodePatchDashboardRequest(ctx context.Context, r *http.Request) (*patchDa
if err := json.NewDecoder(r.Body).Decode(&upd); err != nil {
return nil, errors.MalformedDataf(err.Error())
}
req.Upd = upd
params := httprouter.ParamsFromContext(ctx)
@ -548,7 +543,10 @@ func (r *patchDashboardRequest) Valid() error {
return fmt.Errorf("missing dashboard ID")
}
return r.Upd.Valid()
if pe := r.Upd.Valid(); pe != nil {
return pe
}
return nil
}
type postDashboardCellRequest struct {
@ -595,9 +593,6 @@ func (h *DashboardHandler) handlePostDashboardCell(w http.ResponseWriter, r *htt
return
}
if err := h.DashboardService.AddDashboardCell(ctx, req.dashboardID, req.cell, req.opts); err != nil {
if err == platform.ErrDashboardNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -692,9 +687,6 @@ func (h *DashboardHandler) handleDeleteDashboardCell(w http.ResponseWriter, r *h
return
}
if err := h.DashboardService.RemoveDashboardCell(ctx, req.dashboardID, req.cellID); err != nil {
if err == platform.ErrDashboardNotFound || err == platform.ErrCellNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -714,7 +706,10 @@ func decodePatchDashboardCellRequest(ctx context.Context, r *http.Request) (*pat
params := httprouter.ParamsFromContext(ctx)
id := params.ByName("id")
if id == "" {
return nil, errors.InvalidDataf("url missing id")
return nil, &platform.Error{
Code: platform.EInvalid,
Msg: "url missing id",
}
}
if err := req.dashboardID.DecodeFromString(id); err != nil {
return nil, err
@ -722,18 +717,24 @@ func decodePatchDashboardCellRequest(ctx context.Context, r *http.Request) (*pat
cellID := params.ByName("cellID")
if cellID == "" {
return nil, errors.InvalidDataf("url missing cellID")
return nil, &platform.Error{
Code: platform.EInvalid,
Msg: "cannot provide empty cell id",
}
}
if err := req.cellID.DecodeFromString(cellID); err != nil {
return nil, err
}
if err := json.NewDecoder(r.Body).Decode(&req.upd); err != nil {
return nil, errors.MalformedDataf(err.Error())
return nil, &platform.Error{
Code: platform.EInvalid,
Err: err,
}
}
if err := req.upd.Valid(); err != nil {
return nil, errors.InvalidDataf(err.Error())
if pe := req.upd.Valid(); pe != nil {
return nil, pe
}
return req, nil
@ -750,9 +751,6 @@ func (h *DashboardHandler) handlePatchDashboardCell(w http.ResponseWriter, r *ht
}
cell, err := h.DashboardService.UpdateDashboardCell(ctx, req.dashboardID, req.cellID, req.upd)
if err != nil {
if err == platform.ErrDashboardNotFound || err == platform.ErrCellNotFound {
err = errors.New(err.Error(), errors.NotFound)
}
EncodeError(ctx, err, w)
return
}
@ -768,6 +766,8 @@ type DashboardService struct {
Addr string
Token string
InsecureSkipVerify bool
// OpPrefix is the op prefix for certain errors op.
OpPrefix string
}
// FindDashboardByID returns a single dashboard by ID.
@ -791,7 +791,7 @@ func (s *DashboardService) FindDashboardByID(ctx context.Context, id platform.ID
return nil, err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}
@ -807,9 +807,11 @@ func (s *DashboardService) FindDashboardByID(ctx context.Context, id platform.ID
// FindDashboards returns a list of dashboards that match filter and the total count of matching dashboards.
// Additional options provide pagination & sorting.
func (s *DashboardService) FindDashboards(ctx context.Context, filter platform.DashboardFilter, opts platform.FindOptions) ([]*platform.Dashboard, int, error) {
dashboards := []*platform.Dashboard{}
url, err := newURL(s.Addr, dashboardsPath)
if err != nil {
return nil, 0, err
return dashboards, 0, err
}
qp := url.Query()
@ -821,7 +823,7 @@ func (s *DashboardService) FindDashboards(ctx context.Context, filter platform.D
req, err := http.NewRequest("GET", url.String(), nil)
if err != nil {
return nil, 0, err
return dashboards, 0, err
}
SetToken(s.Token, req)
@ -829,19 +831,19 @@ func (s *DashboardService) FindDashboards(ctx context.Context, filter platform.D
resp, err := hc.Do(req)
if err != nil {
return nil, 0, err
return dashboards, 0, err
}
if err := CheckError(resp); err != nil {
return nil, 0, err
if err := CheckError(resp, true); err != nil {
return dashboards, 0, err
}
var dr getDashboardsResponse
if err := json.NewDecoder(resp.Body).Decode(&dr); err != nil {
return nil, 0, err
return dashboards, 0, err
}
dashboards := dr.toPlatform()
dashboards = dr.toPlatform()
return dashboards, len(dashboards), nil
}
@ -872,7 +874,7 @@ func (s *DashboardService) CreateDashboard(ctx context.Context, d *platform.Dash
return err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return err
}
@ -886,6 +888,7 @@ func (s *DashboardService) CreateDashboard(ctx context.Context, d *platform.Dash
// UpdateDashboard updates a single dashboard with changeset.
// Returns the new dashboard state after update.
func (s *DashboardService) UpdateDashboard(ctx context.Context, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) {
//op := s.OpPrefix + platform.OpUpdateDashboard
u, err := newURL(s.Addr, dashboardIDPath(id))
if err != nil {
return nil, err
@ -911,7 +914,7 @@ func (s *DashboardService) UpdateDashboard(ctx context.Context, id platform.ID,
return nil, err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}
@ -945,7 +948,7 @@ func (s *DashboardService) DeleteDashboard(ctx context.Context, id platform.ID)
if err != nil {
return err
}
return CheckError(resp)
return CheckError(resp, true)
}
// AddDashboardCell adds a cell to a dashboard.
@ -975,7 +978,7 @@ func (s *DashboardService) AddDashboardCell(ctx context.Context, id platform.ID,
return err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return err
}
@ -1001,13 +1004,17 @@ func (s *DashboardService) RemoveDashboardCell(ctx context.Context, dashboardID,
if err != nil {
return err
}
return CheckError(resp)
return CheckError(resp, true)
}
// UpdateDashboardCell replaces the dashboard cell with the provided ID.
func (s *DashboardService) UpdateDashboardCell(ctx context.Context, dashboardID, cellID platform.ID, upd platform.CellUpdate) (*platform.Cell, error) {
op := s.OpPrefix + platform.OpUpdateDashboardCell
if err := upd.Valid(); err != nil {
return nil, err
return nil, &platform.Error{
Op: op,
Err: err,
}
}
u, err := newURL(s.Addr, dashboardCellIDPath(dashboardID, cellID))
@ -1035,7 +1042,7 @@ func (s *DashboardService) UpdateDashboardCell(ctx context.Context, dashboardID,
return nil, err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return nil, err
}
@ -1076,7 +1083,7 @@ func (s *DashboardService) ReplaceDashboardCells(ctx context.Context, id platfor
return err
}
if err := CheckError(resp); err != nil {
if err := CheckError(resp, true); err != nil {
return err
}

View File

@ -290,7 +290,10 @@ func TestService_handleGetDashboard(t *testing.T) {
fields: fields{
&mock.DashboardService{
FindDashboardByIDF: func(ctx context.Context, id platform.ID) (*platform.Dashboard, error) {
return nil, platform.ErrDashboardNotFound
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
},
},
},
@ -509,7 +512,10 @@ func TestService_handleDeleteDashboard(t *testing.T) {
fields: fields{
&mock.DashboardService{
DeleteDashboardF: func(ctx context.Context, id platform.ID) error {
return platform.ErrDashboardNotFound
return &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
},
},
},
@ -679,7 +685,10 @@ func TestService_handlePatchDashboard(t *testing.T) {
fields: fields{
&mock.DashboardService{
UpdateDashboardF: func(ctx context.Context, id platform.ID, upd platform.DashboardUpdate) (*platform.Dashboard, error) {
return nil, platform.ErrDashboardNotFound
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
},
},
},
@ -1086,7 +1095,7 @@ func Test_dashboardCellIDPath(t *testing.T) {
}
}
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, func()) {
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, string, func()) {
t.Helper()
svc := inmem.NewService()
svc.IDGenerator = f.IDGenerator
@ -1109,11 +1118,12 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat
handler.DashboardService = svc
server := httptest.NewServer(handler)
client := DashboardService{
Addr: server.URL,
Addr: server.URL,
OpPrefix: inmem.OpPrefix,
}
done := server.Close
return &client, done
return &client, inmem.OpPrefix, done
}
func TestDashboardService(t *testing.T) {

View File

@ -8,22 +8,35 @@ import (
"github.com/influxdata/platform"
)
func (s *Service) loadDashboard(ctx context.Context, id platform.ID) (*platform.Dashboard, error) {
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, fmt.Errorf("dashboard not found")
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: platform.ErrDashboardNotFound,
}
}
d, ok := i.(*platform.Dashboard)
if !ok {
return nil, fmt.Errorf("type %T is not a dashboard", i)
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) {
return s.loadDashboard(ctx, id)
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 {
@ -43,16 +56,22 @@ func filterDashboardFn(filter platform.DashboardFilter) func(d *platform.Dashboa
// 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 {
return nil, 0, err
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 ds []*platform.Dashboard
var err error
filterF := filterDashboardFn(filter)
s.dashboardKV.Range(func(k, v interface{}) bool {
@ -76,7 +95,14 @@ func (s *Service) FindDashboards(ctx context.Context, filter platform.DashboardF
func (s *Service) CreateDashboard(ctx context.Context, d *platform.Dashboard) error {
d.ID = s.IDGenerator.ID()
d.Meta.CreatedAt = s.time()
return s.PutDashboardWithMeta(ctx, d)
err := s.PutDashboardWithMeta(ctx, d)
if err != nil {
return &platform.Error{
Err: err,
Op: platform.OpCreateDashboard,
}
}
return nil
}
// PutDashboard implements platform.DashboardService interface.
@ -93,21 +119,34 @@ func (s *Service) PutDashboardWithMeta(ctx context.Context, d *platform.Dashboar
// 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, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
d, err := s.FindDashboardByID(ctx, id)
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
if err := upd.Apply(d); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
if err := s.PutDashboardWithMeta(ctx, d); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
return d, nil
@ -115,26 +154,50 @@ func (s *Service) UpdateDashboard(ctx context.Context, id platform.ID, upd platf
// 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 err
return &platform.Error{
Err: err,
Op: op,
}
}
s.dashboardKV.Delete(id.String())
return s.deleteLabel(ctx, platform.LabelFilter{ResourceID: id})
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 err
return &platform.Error{
Err: err,
Op: op,
}
}
cell.ID = s.IDGenerator.ID()
if err := s.createViewIfNotExists(ctx, cell, opts); err != nil {
return err
return &platform.Error{
Err: err,
Op: op,
}
}
d.Cells = append(d.Cells, cell)
return s.PutDashboardWithMeta(ctx, d)
if err = s.PutDashboardWithMeta(ctx, d); err != nil {
return &platform.Error{
Err: err,
Op: op,
}
}
return nil
}
// PutDashboardCell replaces a dashboad cell with the cell contents.
@ -155,9 +218,13 @@ func (s *Service) PutDashboardCell(ctx context.Context, id platform.ID, cell *pl
// 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 err
return &platform.Error{
Err: err,
Op: op,
}
}
idx := -1
@ -168,7 +235,11 @@ func (s *Service) RemoveDashboardCell(ctx context.Context, dashboardID platform.
}
}
if idx == -1 {
return platform.ErrCellNotFound
return &platform.Error{
Code: platform.ENotFound,
Op: op,
Msg: platform.ErrCellNotFound,
}
}
if err := s.DeleteView(ctx, d.Cells[idx].ViewID); err != nil {
@ -182,13 +253,19 @@ func (s *Service) RemoveDashboardCell(ctx context.Context, dashboardID platform.
// 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, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
d, err := s.FindDashboardByID(ctx, dashboardID)
if err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
idx := -1
@ -199,17 +276,27 @@ func (s *Service) UpdateDashboardCell(ctx context.Context, dashboardID platform.
}
}
if idx == -1 {
return nil, platform.ErrCellNotFound
return nil, &platform.Error{
Msg: platform.ErrCellNotFound,
Op: op,
Code: platform.ENotFound,
}
}
if err := upd.Apply(d.Cells[idx]); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
cell := d.Cells[idx]
if err := s.PutDashboardWithMeta(ctx, d); err != nil {
return nil, err
return nil, &platform.Error{
Err: err,
Op: op,
}
}
return cell, nil
@ -217,9 +304,13 @@ func (s *Service) UpdateDashboardCell(ctx context.Context, dashboardID platform.
// 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 err
return &platform.Error{
Err: err,
Op: op,
}
}
ids := map[string]*platform.Cell{}
@ -229,16 +320,28 @@ func (s *Service) ReplaceDashboardCells(ctx context.Context, id platform.ID, cs
for _, cell := range cs {
if !cell.ID.Valid() {
return fmt.Errorf("cannot provide empty cell id")
return &platform.Error{
Code: platform.EInvalid,
Op: op,
Msg: "cannot provide empty cell id",
}
}
cl, ok := ids[cell.ID.String()]
if !ok {
return fmt.Errorf("cannot replace cells that were not already present")
return &platform.Error{
Code: platform.EConflict,
Op: op,
Msg: "cannot replace cells that were not already present",
}
}
if cl.ViewID != cell.ViewID {
return fmt.Errorf("cannot update view id in replace")
return &platform.Error{
Code: platform.EInvalid,
Op: op,
Msg: "cannot update view id in replace",
}
}
}

View File

@ -8,7 +8,7 @@ import (
platformtesting "github.com/influxdata/platform/testing"
)
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, func()) {
func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (platform.DashboardService, string, func()) {
s := NewService()
s.IDGenerator = f.IDGenerator
ctx := context.Background()
@ -23,40 +23,9 @@ func initDashboardService(f platformtesting.DashboardFields, t *testing.T) (plat
t.Fatalf("failed to populate views")
}
}
return s, func() {}
return s, OpPrefix, func() {}
}
func TestDashboardService_CreateDashboard(t *testing.T) {
platformtesting.CreateDashboard(initDashboardService, t)
}
func TestDashboardService_FindDashboardByID(t *testing.T) {
platformtesting.FindDashboardByID(initDashboardService, t)
}
func TestDashboardService_FindDashboards(t *testing.T) {
platformtesting.FindDashboards(initDashboardService, t)
}
func TestDashboardService_DeleteDashboard(t *testing.T) {
platformtesting.DeleteDashboard(initDashboardService, t)
}
func TestDashboardService_UpdateDashboard(t *testing.T) {
platformtesting.UpdateDashboard(initDashboardService, t)
}
func TestDashboardService_AddDashboardCell(t *testing.T) {
platformtesting.AddDashboardCell(initDashboardService, t)
}
func TestDashboardService_RemoveDashboardCell(t *testing.T) {
platformtesting.RemoveDashboardCell(initDashboardService, t)
}
func TestDashboardService_UpdateDashboardCell(t *testing.T) {
platformtesting.UpdateDashboardCell(initDashboardService, t)
}
func TestDashboardService_ReplaceDashboardCells(t *testing.T) {
platformtesting.ReplaceDashboardCells(initDashboardService, t)
func TestDashboardService(t *testing.T) {
platformtesting.DashboardService(initDashboardService, t)
}

View File

@ -3,7 +3,6 @@ package testing
import (
"bytes"
"context"
"fmt"
"testing"
"time"
@ -24,10 +23,10 @@ func idPtr(id platform.ID) *platform.ID {
}
var dashboardCmpOptions = cmp.Options{
cmpopts.EquateEmpty(),
cmp.Comparer(func(x, y []byte) bool {
return bytes.Equal(x, y)
}),
cmpopts.EquateEmpty(),
}
// DashboardFields will include the IDGenerator, and dashboards
@ -40,11 +39,11 @@ type DashboardFields struct {
// DashboardService tests all the service functions.
func DashboardService(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()), t *testing.T,
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()), t *testing.T,
) {
tests := []struct {
name string
fn func(init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
fn func(init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T)
}{
{
@ -63,6 +62,10 @@ func DashboardService(
name: "UpdateDashboard",
fn: UpdateDashboard,
},
{
name: "DeleteDashboard",
fn: DeleteDashboard,
},
{
name: "AddDashboardCell",
fn: AddDashboardCell,
@ -89,7 +92,7 @@ func DashboardService(
// CreateDashboard testing
func CreateDashboard(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -187,19 +190,12 @@ func CreateDashboard(
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
err := s.CreateDashboard(ctx, tt.args.dashboard)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
defer s.DeleteDashboard(ctx, tt.args.dashboard.ID)
dashboards, _, err := s.FindDashboards(ctx, platform.DashboardFilter{}, platform.DefaultDashboardFindOptions)
@ -215,7 +211,7 @@ func CreateDashboard(
// AddDashboardCell testing
func AddDashboardCell(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -328,23 +324,59 @@ func AddDashboardCell(
},
},
},
{
name: "add cell with id not exist",
fields: DashboardFields{
NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) },
IDGenerator: &mock.IDGenerator{
IDFn: func() platform.ID {
return MustIDBase16(dashTwoID)
},
},
Dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
},
},
Views: []*platform.View{
{
ViewContents: platform.ViewContents{
ID: MustIDBase16(dashTwoID),
},
},
},
},
args: args{
dashboardID: MustIDBase16(threeID),
cell: &platform.Cell{
ViewID: MustIDBase16(dashTwoID),
},
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpAddDashboardCell,
Msg: platform.ErrDashboardNotFound,
},
dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
err := s.AddDashboardCell(ctx, tt.args.dashboardID, tt.args.cell, platform.AddDashboardCellOptions{})
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
defer s.DeleteDashboard(ctx, tt.args.dashboardID)
dashboards, _, err := s.FindDashboards(ctx, platform.DashboardFilter{}, platform.DefaultDashboardFindOptions)
@ -360,7 +392,7 @@ func AddDashboardCell(
// FindDashboardByID testing
func FindDashboardByID(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -401,24 +433,41 @@ func FindDashboardByID(
},
},
},
{
name: "find dashboard by id not exists",
fields: DashboardFields{
Dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
},
{
ID: MustIDBase16(dashTwoID),
Name: "dashboard2",
},
},
},
args: args{
id: MustIDBase16(threeID),
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpFindDashboardByID,
Msg: platform.ErrDashboardNotFound,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
dashboard, err := s.FindDashboardByID(ctx, tt.args.id)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected errors to be equal '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(dashboard, tt.wants.dashboard, dashboardCmpOptions...); diff != "" {
t.Errorf("dashboard is different -got/+want\ndiff %s", diff)
@ -429,7 +478,7 @@ func FindDashboardByID(
// FindDashboards testing
func FindDashboards(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -628,11 +677,35 @@ func FindDashboards(
},
},
},
{
name: "find multiple dashboards by id not exists",
fields: DashboardFields{
Dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "abc",
},
{
ID: MustIDBase16(dashTwoID),
Name: "xyz",
},
},
},
args: args{
IDs: []*platform.ID{
idPtr(MustIDBase16(threeID)),
},
findOptions: platform.DefaultDashboardFindOptions,
},
wants: wants{
dashboards: []*platform.Dashboard{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
@ -642,15 +715,7 @@ func FindDashboards(
}
dashboards, _, err := s.FindDashboards(ctx, filter, tt.args.findOptions)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected errors to be equal '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(dashboards, tt.wants.dashboards, dashboardCmpOptions...); diff != "" {
t.Errorf("dashboards are different -got/+want\ndiff %s", diff)
@ -661,7 +726,7 @@ func FindDashboards(
// DeleteDashboard testing
func DeleteDashboard(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -722,7 +787,11 @@ func DeleteDashboard(
ID: MustIDBase16(dashThreeID),
},
wants: wants{
err: fmt.Errorf("dashboard not found"),
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpDeleteDashboard,
Msg: platform.ErrDashboardNotFound,
},
dashboards: []*platform.Dashboard{
{
Name: "A",
@ -739,19 +808,11 @@ func DeleteDashboard(
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
err := s.DeleteDashboard(ctx, tt.args.ID)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
filter := platform.DashboardFilter{}
dashboards, _, err := s.FindDashboards(ctx, filter, platform.DefaultDashboardFindOptions)
@ -767,7 +828,7 @@ func DeleteDashboard(
// UpdateDashboard testing
func UpdateDashboard(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -876,11 +937,39 @@ func UpdateDashboard(
},
},
},
{
name: "update with id not exist",
fields: DashboardFields{
NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) },
Dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
},
{
ID: MustIDBase16(dashTwoID),
Name: "dashboard2",
},
},
},
args: args{
id: MustIDBase16(threeID),
description: "changed",
name: "changed",
},
wants: wants{
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpUpdateDashboard,
Msg: platform.ErrDashboardNotFound,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
@ -893,15 +982,7 @@ func UpdateDashboard(
}
dashboard, err := s.UpdateDashboard(ctx, tt.args.id, upd)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if diff := cmp.Diff(dashboard, tt.wants.dashboard, dashboardCmpOptions...); diff != "" {
t.Errorf("dashboard is different -got/+want\ndiff %s", diff)
@ -912,7 +993,7 @@ func UpdateDashboard(
// RemoveDashboardCell testing
func RemoveDashboardCell(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -989,19 +1070,12 @@ func RemoveDashboardCell(
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
err := s.RemoveDashboardCell(ctx, tt.args.dashboardID, tt.args.cellID)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
defer s.DeleteDashboard(ctx, tt.args.dashboardID)
dashboards, _, err := s.FindDashboards(ctx, platform.DashboardFilter{}, platform.DefaultDashboardFindOptions)
@ -1017,7 +1091,7 @@ func RemoveDashboardCell(
// UpdateDashboardCell testing
func UpdateDashboardCell(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -1094,7 +1168,7 @@ func UpdateDashboardCell(
},
},
{
name: "invalid cell update",
name: "invalid cell update without attribute",
fields: DashboardFields{
NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) },
IDGenerator: &mock.IDGenerator{
@ -1141,26 +1215,80 @@ func UpdateDashboardCell(
},
},
},
err: fmt.Errorf("must update at least one attribute"),
err: &platform.Error{
Code: platform.EInvalid,
Op: platform.OpUpdateDashboardCell,
Msg: "must update at least one attribute",
},
},
},
{
name: "invalid cell update cell id not exist",
fields: DashboardFields{
NowFn: func() time.Time { return time.Date(2009, time.November, 10, 24, 0, 0, 0, time.UTC) },
IDGenerator: &mock.IDGenerator{
IDFn: func() platform.ID {
return MustIDBase16(dashTwoID)
},
},
Dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
Cells: []*platform.Cell{
{
ID: MustIDBase16(dashTwoID),
ViewID: MustIDBase16(dashTwoID),
},
{
ID: MustIDBase16(dashOneID),
ViewID: MustIDBase16(dashOneID),
},
},
},
},
},
args: args{
dashboardID: MustIDBase16(dashOneID),
cellID: MustIDBase16(fourID),
cellUpdate: platform.CellUpdate{
ViewID: MustIDBase16(threeID),
},
},
wants: wants{
dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
Name: "dashboard1",
Cells: []*platform.Cell{
{
ID: MustIDBase16(dashTwoID),
ViewID: MustIDBase16(dashTwoID),
},
{
ID: MustIDBase16(dashOneID),
ViewID: MustIDBase16(dashOneID),
},
},
},
},
err: &platform.Error{
Code: platform.ENotFound,
Op: platform.OpUpdateDashboardCell,
Msg: platform.ErrCellNotFound,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
_, err := s.UpdateDashboardCell(ctx, tt.args.dashboardID, tt.args.cellID, tt.args.cellUpdate)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
defer s.DeleteDashboard(ctx, tt.args.dashboardID)
dashboards, _, err := s.FindDashboards(ctx, platform.DashboardFilter{}, platform.DefaultDashboardFindOptions)
@ -1176,7 +1304,7 @@ func UpdateDashboardCell(
// ReplaceDashboardCells testing
func ReplaceDashboardCells(
init func(DashboardFields, *testing.T) (platform.DashboardService, func()),
init func(DashboardFields, *testing.T) (platform.DashboardService, string, func()),
t *testing.T,
) {
type args struct {
@ -1316,7 +1444,11 @@ func ReplaceDashboardCells(
},
},
wants: wants{
err: fmt.Errorf("cannot replace cells that were not already present"),
err: &platform.Error{
Code: platform.EConflict,
Op: platform.OpReplaceDashboardCells,
Msg: "cannot replace cells that were not already present",
},
dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
@ -1376,7 +1508,11 @@ func ReplaceDashboardCells(
},
},
wants: wants{
err: fmt.Errorf("cannot update view id in replace"),
err: &platform.Error{
Code: platform.EInvalid,
Op: platform.OpReplaceDashboardCells,
Msg: "cannot update view id in replace",
},
dashboards: []*platform.Dashboard{
{
ID: MustIDBase16(dashOneID),
@ -1395,19 +1531,12 @@ func ReplaceDashboardCells(
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s, done := init(tt.fields, t)
s, opPrefix, done := init(tt.fields, t)
defer done()
ctx := context.Background()
err := s.ReplaceDashboardCells(ctx, tt.args.dashboardID, tt.args.cells)
if (err != nil) != (tt.wants.err != nil) {
t.Fatalf("expected error '%v' got '%v'", tt.wants.err, err)
}
diffPlatformErrors(tt.name, err, tt.wants.err, opPrefix, t)
if err != nil && tt.wants.err != nil {
if err.Error() != tt.wants.err.Error() {
t.Fatalf("expected error messages to match '%v' got '%v'", tt.wants.err, err.Error())
}
}
defer s.DeleteDashboard(ctx, tt.args.dashboardID)
dashboards, _, err := s.FindDashboards(ctx, platform.DashboardFilter{}, platform.DefaultDashboardFindOptions)