From 6ca7c68e2ef2a5dcda83840aaa45000cc74e78ae Mon Sep 17 00:00:00 2001 From: Kelvin Wang Date: Tue, 11 Dec 2018 13:38:24 -0500 Subject: [PATCH] fix(http): convert dashboard errors --- bolt/dashboard.go | 183 ++++++++++++++++----- bolt/dashboard_test.go | 41 +---- dashboard.go | 38 +++-- http/dashboard_service.go | 97 ++++++----- http/dashboard_test.go | 22 ++- inmem/dashboard.go | 163 +++++++++++++++---- inmem/dashboard_test.go | 39 +---- testing/dashboards.go | 335 ++++++++++++++++++++++++++------------ 8 files changed, 615 insertions(+), 303 deletions(-) diff --git a/bolt/dashboard.go b/bolt/dashboard.go index cac16b1165..e30f6aa307 100644 --- a/bolt/dashboard.go +++ b/bolt/dashboard.go @@ -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" diff --git a/bolt/dashboard_test.go b/bolt/dashboard_test.go index 1a3e9e1829..b5260dbd67 100644 --- a/bolt/dashboard_test.go +++ b/bolt/dashboard_test.go @@ -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) } diff --git a/dashboard.go b/dashboard.go index fbe71eb33d..efd0cc514d 100644 --- a/dashboard.go +++ b/dashboard.go @@ -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 diff --git a/http/dashboard_service.go b/http/dashboard_service.go index ca81740c2a..eb535bf4b2 100644 --- a/http/dashboard_service.go +++ b/http/dashboard_service.go @@ -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 } diff --git a/http/dashboard_test.go b/http/dashboard_test.go index 7ca2e78c76..f66241661c 100644 --- a/http/dashboard_test.go +++ b/http/dashboard_test.go @@ -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) { diff --git a/inmem/dashboard.go b/inmem/dashboard.go index 4d081d8598..f7d8824c5f 100644 --- a/inmem/dashboard.go +++ b/inmem/dashboard.go @@ -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", + } } } diff --git a/inmem/dashboard_test.go b/inmem/dashboard_test.go index 443113e911..ee275918a6 100644 --- a/inmem/dashboard_test.go +++ b/inmem/dashboard_test.go @@ -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) } diff --git a/testing/dashboards.go b/testing/dashboards.go index d413454362..d51d64036d 100644 --- a/testing/dashboards.go +++ b/testing/dashboards.go @@ -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)