From b23560ed5a33e76eda5292cff7b231a9ca3d98b3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 31 Jan 2018 16:51:06 +0100 Subject: [PATCH] dashboards: add validation to delete dashboard by slug Validates that there are only one folder/dashboard having that slug, otherwise returns 412 Precondition Failed --- pkg/api/dashboard.go | 10 +++++++ pkg/api/dashboard_test.go | 43 ++++++++++++++++++++++++++++++ pkg/models/dashboards.go | 10 ++++++- pkg/services/sqlstore/dashboard.go | 11 ++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index ae8d819b981..7b3977df0d8 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -146,6 +146,16 @@ func getDashboardHelper(orgId int64, slug string, id int64, uid string) (*m.Dash } func DeleteDashboard(c *middleware.Context) Response { + query := m.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: c.Params(":slug")} + + if err := bus.Dispatch(&query); err != nil { + return ApiError(500, "Failed to retrieve dashboards by slug", err) + } + + if len(query.Result) > 1 { + return Json(412, util.DynMap{"status": "multiple-slugs-exists", "message": m.ErrDashboardsWithSameSlugExists.Error()}) + } + dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, "") if rsp != nil { return rsp diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 3d8f49b5f23..5ff9efc84e6 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -39,6 +39,12 @@ func TestDashboardApiEndpoint(t *testing.T) { fakeDash.FolderId = 1 fakeDash.HasAcl = false + bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { + dashboards := []*m.Dashboard{fakeDash} + query.Result = dashboards + return nil + }) + var getDashboardQueries []*m.GetDashboardQuery bus.AddHandler("test", func(query *m.GetDashboardQuery) error { @@ -232,6 +238,12 @@ func TestDashboardApiEndpoint(t *testing.T) { fakeDash.HasAcl = true setting.ViewersCanEdit = false + bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { + dashboards := []*m.Dashboard{fakeDash} + query.Result = dashboards + return nil + }) + aclMockResp := []*m.DashboardAclInfoDTO{ { DashboardId: 1, @@ -671,6 +683,37 @@ func TestDashboardApiEndpoint(t *testing.T) { }) }) }) + + Convey("Given two dashboards with the same title in different folders", t, func() { + dashOne := m.NewDashboard("dash") + dashOne.Id = 2 + dashOne.FolderId = 1 + dashOne.HasAcl = false + + dashTwo := m.NewDashboard("dash") + dashTwo.Id = 4 + dashTwo.FolderId = 3 + dashTwo.HasAcl = false + + bus.AddHandler("test", func(query *m.GetDashboardsBySlugQuery) error { + dashboards := []*m.Dashboard{dashOne, dashTwo} + query.Result = dashboards + return nil + }) + + role := m.ROLE_EDITOR + + loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/db/dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) { + CallDeleteDashboard(sc) + + Convey("Should result in 412 Precondition failed", func() { + So(sc.resp.Code, ShouldEqual, 412) + result := sc.ToJson() + So(result.Get("status").MustString(), ShouldEqual, "multiple-slugs-exists") + So(result.Get("message").MustString(), ShouldEqual, m.ErrDashboardsWithSameSlugExists.Error()) + }) + }) + }) } func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta { diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 84ede04f226..dc296d2b3bd 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -22,6 +22,7 @@ var ( ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder") ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard") ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data") + ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists") ) type UpdatePluginDashboardError struct { @@ -165,7 +166,7 @@ func GetDashboardUrl(uid string, slug string) string { // GetFolderUrl return the html url for a folder func GetFolderUrl(folderUid string, slug string) string { - return fmt.Sprintf("%s/f/%v/%s", setting.AppSubUrl, folderUid, slug) + return fmt.Sprintf("%s/dashboards/f/%s/%s", setting.AppSubUrl, folderUid, slug) } // @@ -231,3 +232,10 @@ type GetDashboardSlugByIdQuery struct { Id int64 Result string } + +type GetDashboardsBySlugQuery struct { + OrgId int64 + Slug string + + Result []*Dashboard +} diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 84cfd1999c7..43e9d03e649 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -386,3 +386,14 @@ func GetDashboardSlugById(query *m.GetDashboardSlugByIdQuery) error { query.Result = slug.Slug return nil } + +func GetDashboardsBySlug(query *m.GetDashboardsBySlugQuery) error { + var dashboards = make([]*m.Dashboard, 0) + + if err := x.Where("org_id=? AND slug=?", query.OrgId, query.Slug).Find(&dashboards); err != nil { + return err + } + + query.Result = dashboards + return nil +}