Pubdash: Email sharing handle dashboard deleted (#64247)

dashboard service calls pubdash service when dashboard deleted
This commit is contained in:
owensmallwood 2023-03-08 14:54:35 -06:00 committed by GitHub
parent 154fa2dd00
commit 1a5a280c86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 251 additions and 152 deletions

View File

@ -326,6 +326,12 @@ func (hs *HTTPServer) deleteDashboard(c *contextmodel.ReqContext) response.Respo
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.ID, "user", c.SignedInUser.UserID, "error", err)
}
// deletes all related public dashboard entities
err = hs.PublicDashboardsApi.PublicDashboardService.DeleteByDashboard(c.Req.Context(), dash)
if err != nil {
hs.log.Error("Failed to delete public dashboard")
}
err = hs.DashboardService.DeleteDashboard(c.Req.Context(), dash.ID, c.OrgID)
if err != nil {
var dashboardErr dashboards.DashboardErr

View File

@ -9,6 +9,8 @@ import (
"os"
"testing"
"github.com/grafana/grafana/pkg/services/publicdashboards"
"github.com/grafana/grafana/pkg/services/publicdashboards/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@ -304,7 +306,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUp()
sc.sqlStore = mockSQLStore
hs.callDeleteDashboardByUID(t, sc, dashboardService)
hs.callDeleteDashboardByUID(t, sc, dashboardService, nil)
assert.Equal(t, 403, sc.resp.Code)
}, mockSQLStore)
@ -342,7 +344,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi",
"/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUp()
hs.callDeleteDashboardByUID(t, sc, dashboardService)
hs.callDeleteDashboardByUID(t, sc, dashboardService, nil)
assert.Equal(t, 403, sc.resp.Code)
}, mockSQLStore)
@ -400,23 +402,9 @@ func TestDashboardAPIEndpoint(t *testing.T) {
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil)
hs.callDeleteDashboardByUID(t, sc, dashboardService)
assert.Equal(t, 200, sc.resp.Code)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions/1", "/api/dashboards/id/:dashboardId/versions/:id", role, func(sc *scenarioContext) {
setUpInner()
sc.sqlStore = mockSQLStore
sc.dashboardVersionService = fakeDashboardVersionService
hs.callGetDashboardVersion(sc)
assert.Equal(t, 200, sc.resp.Code)
}, mockSQLStore)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/2/versions", "/api/dashboards/id/:dashboardId/versions", role, func(sc *scenarioContext) {
setUpInner()
hs.callGetDashboardVersions(sc)
pubdashService := publicdashboards.NewFakePublicDashboardService(t)
pubdashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil)
hs.callDeleteDashboardByUID(t, sc, dashboardService, pubdashService)
assert.Equal(t, 200, sc.resp.Code)
}, mockSQLStore)
@ -455,7 +443,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner()
hs.callDeleteDashboardByUID(t, sc, dashboardService)
hs.callDeleteDashboardByUID(t, sc, dashboardService, nil)
assert.Equal(t, 403, sc.resp.Code)
}, mockSQLStore)
})
@ -495,7 +483,9 @@ func TestDashboardAPIEndpoint(t *testing.T) {
qResult := dashboards.NewDashboard("test")
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
dashboardService.On("DeleteDashboard", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("int64")).Return(nil)
hs.callDeleteDashboardByUID(t, sc, dashboardService)
pubdashService := publicdashboards.NewFakePublicDashboardService(t)
pubdashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil)
hs.callDeleteDashboardByUID(t, sc, dashboardService, pubdashService)
assert.Equal(t, 200, sc.resp.Code)
}, mockSQLStore)
@ -538,7 +528,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
setUpInner()
hs.callDeleteDashboardByUID(t, sc, dashboardService)
hs.callDeleteDashboardByUID(t, sc, dashboardService, nil)
assert.Equal(t, 403, sc.resp.Code)
}, mockSQLStore)
@ -1035,8 +1025,10 @@ func (hs *HTTPServer) callGetDashboardVersions(sc *scenarioContext) {
}
func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T,
sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService) {
sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService, mockPubdashService *publicdashboards.FakePublicDashboardService) {
hs.DashboardService = mockDashboard
pubdashApi := api.ProvideApi(mockPubdashService, nil, nil, featuremgmt.WithFeatures())
hs.PublicDashboardsApi = pubdashApi
sc.handlerFunc = hs.DeleteDashboardByUID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
}

View File

@ -702,7 +702,6 @@ func (d *dashboardStore) deleteDashboard(cmd *dashboards.DeleteDashboardCommand,
deletes := []string{
"DELETE FROM dashboard_tag WHERE dashboard_id = ? ",
"DELETE FROM star WHERE dashboard_id = ? ",
"DELETE FROM dashboard_public WHERE dashboard_uid = (SELECT uid FROM dashboard WHERE id = ?)",
"DELETE FROM dashboard WHERE id = ?",
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
@ -751,7 +750,6 @@ func (d *dashboardStore) deleteDashboard(cmd *dashboards.DeleteDashboardCommand,
"DELETE FROM annotation WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
"DELETE FROM dashboard_provisioning WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
"DELETE FROM dashboard_acl WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
"DELETE FROM dashboard_public WHERE dashboard_uid IN (SELECT uid FROM dashboard WHERE org_id = ? AND folder_id = ?)",
}
for _, sql := range childrenDeletes {
_, err := sess.Exec(sql, dashboard.OrgID, dashboard.ID)

View File

@ -16,8 +16,6 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/publicdashboards/database"
publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/sqlstore"
@ -27,7 +25,6 @@ import (
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
func TestIntegrationDashboardDataAccess(t *testing.T) {
@ -39,7 +36,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
var savedFolder, savedDash, savedDash2 *dashboards.Dashboard
var dashboardStore dashboards.Store
var starService star.Service
var publicDashboardStore *database.PublicDashboardStoreImpl
setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t)
@ -53,8 +49,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
insertTestDashboard(t, dashboardStore, "test dash 45", 1, savedFolder.ID, false, "prod")
savedDash2 = insertTestDashboard(t, dashboardStore, "test dash 67", 1, 0, false, "prod")
insertTestRule(t, sqlStore, savedFolder.OrgID, savedFolder.UID)
publicDashboardStore = database.ProvideStore(sqlStore)
}
t.Run("Should return dashboard model", func(t *testing.T) {
@ -246,78 +240,6 @@ func TestIntegrationDashboardDataAccess(t *testing.T) {
require.True(t, errors.Is(err, dashboards.ErrFolderContainsAlertRules))
})
t.Run("Should be able to delete dashboard and related public dashboard", func(t *testing.T) {
setup()
uid := util.GenerateShortUID()
cmd := publicDashboardModels.SavePublicDashboardCommand{
PublicDashboard: publicDashboardModels.PublicDashboard{
Uid: uid,
DashboardUid: savedDash.UID,
OrgId: savedDash.OrgID,
IsEnabled: true,
TimeSettings: &publicDashboardModels.TimeSettings{},
CreatedBy: 1,
CreatedAt: time.Now(),
AccessToken: "an-access-token",
},
}
_, err := publicDashboardStore.Create(context.Background(), cmd)
require.NoError(t, err)
pubdashConfig, _ := publicDashboardStore.FindByAccessToken(context.Background(), "an-access-token")
require.NotNil(t, pubdashConfig)
deleteCmd := &dashboards.DeleteDashboardCommand{ID: savedDash.ID, OrgID: savedDash.OrgID}
err = dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
require.NoError(t, err)
query := dashboards.GetDashboardQuery{UID: savedDash.UID, OrgID: savedDash.OrgID}
dash, getErr := dashboardStore.GetDashboard(context.Background(), &query)
require.Equal(t, getErr, dashboards.ErrDashboardNotFound)
assert.Nil(t, dash)
pubdashConfig, err = publicDashboardStore.FindByAccessToken(context.Background(), "an-access-token")
require.Nil(t, err)
require.Nil(t, pubdashConfig)
})
t.Run("Should be able to delete a dashboard folder, with its dashboard and related public dashboard", func(t *testing.T) {
setup()
uid := util.GenerateShortUID()
cmd := publicDashboardModels.SavePublicDashboardCommand{
PublicDashboard: publicDashboardModels.PublicDashboard{
Uid: uid,
DashboardUid: savedDash.UID,
OrgId: savedDash.OrgID,
IsEnabled: true,
TimeSettings: &publicDashboardModels.TimeSettings{},
CreatedBy: 1,
CreatedAt: time.Now(),
AccessToken: "an-access-token",
},
}
_, err := publicDashboardStore.Create(context.Background(), cmd)
require.NoError(t, err)
pubdashConfig, _ := publicDashboardStore.FindByAccessToken(context.Background(), "an-access-token")
require.NotNil(t, pubdashConfig)
deleteCmd := &dashboards.DeleteDashboardCommand{ID: savedFolder.ID, ForceDeleteFolderRules: true}
err = dashboardStore.DeleteDashboard(context.Background(), deleteCmd)
require.NoError(t, err)
query := dashboards.GetDashboardsQuery{
DashboardIDs: []int64{savedFolder.ID, savedDash.ID},
}
queryResult, err := dashboardStore.GetDashboards(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, len(queryResult), 0)
pubdashConfig, err = publicDashboardStore.FindByAccessToken(context.Background(), "an-access-token")
require.Nil(t, err)
require.Nil(t, pubdashConfig)
})
t.Run("Should be able to delete a dashboard folder and its children if force delete rules is enabled", func(t *testing.T) {
setup()
deleteCmd := &dashboards.DeleteDashboardCommand{ID: savedFolder.ID, ForceDeleteFolderRules: true}

View File

@ -196,7 +196,7 @@ func (api *Api) DeletePublicDashboard(c *contextmodel.ReqContext) response.Respo
return response.Err(ErrInvalidUid.Errorf("UpdatePublicDashboard: invalid Uid %s", uid))
}
err := api.PublicDashboardService.Delete(c.Req.Context(), c.OrgID, uid)
err := api.PublicDashboardService.Delete(c.Req.Context(), uid)
if err != nil {
return response.Err(err)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/publicdashboards"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
// Define the storage implementation. We're generating the mock implementation
@ -255,8 +256,8 @@ func (d *PublicDashboardStoreImpl) Update(ctx context.Context, cmd SavePublicDas
}
// Deletes a public dashboard
func (d *PublicDashboardStoreImpl) Delete(ctx context.Context, orgId int64, uid string) (int64, error) {
dashboard := &PublicDashboard{OrgId: orgId, Uid: uid}
func (d *PublicDashboardStoreImpl) Delete(ctx context.Context, uid string) (int64, error) {
dashboard := &PublicDashboard{Uid: uid}
var affectedRows int64
err := d.sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
var err error
@ -267,3 +268,20 @@ func (d *PublicDashboardStoreImpl) Delete(ctx context.Context, orgId int64, uid
return affectedRows, err
}
func (d *PublicDashboardStoreImpl) FindByDashboardFolder(ctx context.Context, dashboard *dashboards.Dashboard) ([]*PublicDashboard, error) {
if dashboard == nil || !dashboard.IsFolder {
return nil, nil
}
var pubdashes []*PublicDashboard
err := d.sqlStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
return sess.SQL("SELECT * from dashboard_public WHERE (dashboard_uid, org_id) IN (SELECT uid, org_id FROM dashboard WHERE folder_id = ?)", dashboard.ID).Find(&pubdashes)
})
if err != nil {
return nil, err
}
return pubdashes, nil
}

View File

@ -652,7 +652,7 @@ func TestIntegrationDelete(t *testing.T) {
t.Run("Delete success", func(t *testing.T) {
setup()
// Do the deletion
affectedRows, err := publicdashboardStore.Delete(context.Background(), savedPublicDashboard.OrgId, savedPublicDashboard.Uid)
affectedRows, err := publicdashboardStore.Delete(context.Background(), savedPublicDashboard.Uid)
require.NoError(t, err)
assert.EqualValues(t, affectedRows, 1)
@ -665,12 +665,51 @@ func TestIntegrationDelete(t *testing.T) {
t.Run("Non-existent public dashboard deletion doesn't throw an error", func(t *testing.T) {
setup()
affectedRows, err := publicdashboardStore.Delete(context.Background(), 15, "non-existent-uid")
affectedRows, err := publicdashboardStore.Delete(context.Background(), "non-existent-uid")
require.NoError(t, err)
assert.EqualValues(t, affectedRows, 0)
})
}
func TestGetDashboardByFolder(t *testing.T) {
t.Run("returns nil when dashboard is not a folder", func(t *testing.T) {
sqlStore, _ := db.InitTestDBwithCfg(t)
dashboard := &dashboards.Dashboard{IsFolder: false}
store := ProvideStore(sqlStore)
pubdashes, err := store.FindByDashboardFolder(context.Background(), dashboard)
require.NoError(t, err)
assert.Nil(t, pubdashes)
})
t.Run("returns nil when dashboard is nil", func(t *testing.T) {
sqlStore, _ := db.InitTestDBwithCfg(t)
store := ProvideStore(sqlStore)
pubdashes, err := store.FindByDashboardFolder(context.Background(), nil)
require.NoError(t, err)
assert.Nil(t, pubdashes)
})
t.Run("can get all pubdashes for dashboard folder and org", func(t *testing.T) {
sqlStore, cfg := db.InitTestDBwithCfg(t)
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
pubdashStore := ProvideStore(sqlStore)
dashboard := insertTestDashboard(t, dashboardStore, "title", 1, 1, true)
pubdash := insertPublicDashboard(t, pubdashStore, dashboard.UID, dashboard.OrgID, true)
dashboard2 := insertTestDashboard(t, dashboardStore, "title", 1, 2, true)
_ = insertPublicDashboard(t, pubdashStore, dashboard2.UID, dashboard2.OrgID, true)
pubdashes, err := pubdashStore.FindByDashboardFolder(context.Background(), dashboard)
require.NoError(t, err)
assert.Len(t, pubdashes, 1)
assert.Equal(t, pubdash, pubdashes[0])
})
}
// helper function to insert a dashboard
func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title string, orgId int64,
folderId int64, isFolder bool, tags ...interface{}) *dashboards.Dashboard {

View File

@ -1,4 +1,4 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
// Code generated by mockery v2.16.0. DO NOT EDIT.
package publicdashboards
@ -46,13 +46,27 @@ func (_m *FakePublicDashboardService) Create(ctx context.Context, u *user.Signed
return r0, r1
}
// Delete provides a mock function with given fields: ctx, orgId, uid
func (_m *FakePublicDashboardService) Delete(ctx context.Context, orgId int64, uid string) error {
ret := _m.Called(ctx, orgId, uid)
// Delete provides a mock function with given fields: ctx, uid
func (_m *FakePublicDashboardService) Delete(ctx context.Context, uid string) error {
ret := _m.Called(ctx, uid)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok {
r0 = rf(ctx, orgId, uid)
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, uid)
} else {
r0 = ret.Error(0)
}
return r0
}
// DeleteByDashboard provides a mock function with given fields: ctx, dashboard
func (_m *FakePublicDashboardService) DeleteByDashboard(ctx context.Context, dashboard *dashboards.Dashboard) error {
ret := _m.Called(ctx, dashboard)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard) error); ok {
r0 = rf(ctx, dashboard)
} else {
r0 = ret.Error(0)
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
// Code generated by mockery v2.16.0. DO NOT EDIT.
package publicdashboards
@ -14,6 +14,20 @@ type FakePublicDashboardServiceWrapper struct {
mock.Mock
}
// Delete provides a mock function with given fields: ctx, uid
func (_m *FakePublicDashboardServiceWrapper) Delete(ctx context.Context, uid string) error {
ret := _m.Called(ctx, uid)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, uid)
} else {
r0 = ret.Error(0)
}
return r0
}
// FindByDashboardUid provides a mock function with given fields: ctx, orgId, dashboardUid
func (_m *FakePublicDashboardServiceWrapper) FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboard, error) {
ret := _m.Called(ctx, orgId, dashboardUid)

View File

@ -1,4 +1,4 @@
// Code generated by mockery v2.14.0. DO NOT EDIT.
// Code generated by mockery v2.16.0. DO NOT EDIT.
package publicdashboards
@ -37,20 +37,20 @@ func (_m *FakePublicDashboardStore) Create(ctx context.Context, cmd models.SaveP
return r0, r1
}
// Delete provides a mock function with given fields: ctx, orgId, uid
func (_m *FakePublicDashboardStore) Delete(ctx context.Context, orgId int64, uid string) (int64, error) {
ret := _m.Called(ctx, orgId, uid)
// Delete provides a mock function with given fields: ctx, uid
func (_m *FakePublicDashboardStore) Delete(ctx context.Context, uid string) (int64, error) {
ret := _m.Called(ctx, uid)
var r0 int64
if rf, ok := ret.Get(0).(func(context.Context, int64, string) int64); ok {
r0 = rf(ctx, orgId, uid)
if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok {
r0 = rf(ctx, uid)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
r1 = rf(ctx, orgId, uid)
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, uid)
} else {
r1 = ret.Error(1)
}
@ -169,6 +169,29 @@ func (_m *FakePublicDashboardStore) FindByAccessToken(ctx context.Context, acces
return r0, r1
}
// FindByDashboardFolder provides a mock function with given fields: ctx, dashboard
func (_m *FakePublicDashboardStore) FindByDashboardFolder(ctx context.Context, dashboard *dashboards.Dashboard) ([]*models.PublicDashboard, error) {
ret := _m.Called(ctx, dashboard)
var r0 []*models.PublicDashboard
if rf, ok := ret.Get(0).(func(context.Context, *dashboards.Dashboard) []*models.PublicDashboard); ok {
r0 = rf(ctx, dashboard)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*models.PublicDashboard)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *dashboards.Dashboard) error); ok {
r1 = rf(ctx, dashboard)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FindByDashboardUid provides a mock function with given fields: ctx, orgId, dashboardUid
func (_m *FakePublicDashboardStore) FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*models.PublicDashboard, error) {
ret := _m.Called(ctx, orgId, dashboardUid)

View File

@ -24,7 +24,8 @@ type Service interface {
Find(ctx context.Context, uid string) (*PublicDashboard, error)
Create(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardDTO) (*PublicDashboard, error)
Update(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardDTO) (*PublicDashboard, error)
Delete(ctx context.Context, orgId int64, uid string) error
Delete(ctx context.Context, uid string) error
DeleteByDashboard(ctx context.Context, dashboard *dashboards.Dashboard) error
GetMetricRequest(ctx context.Context, dashboard *dashboards.Dashboard, publicDashboard *PublicDashboard, panelId int64, reqDTO PublicDashboardQueryDTO) (dtos.MetricRequest, error)
GetQueryDataResponse(ctx context.Context, skipCache bool, reqDTO PublicDashboardQueryDTO, panelId int64, accessToken string) (*backend.QueryDataResponse, error)
@ -41,6 +42,7 @@ type Service interface {
//go:generate mockery --name ServiceWrapper --structname FakePublicDashboardServiceWrapper --inpackage --filename public_dashboard_service_wrapper_mock.go
type ServiceWrapper interface {
FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
Delete(ctx context.Context, uid string) error
}
//go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go
@ -52,9 +54,10 @@ type Store interface {
FindAll(ctx context.Context, orgId int64) ([]PublicDashboardListResponse, error)
Create(ctx context.Context, cmd SavePublicDashboardCommand) (int64, error)
Update(ctx context.Context, cmd SavePublicDashboardCommand) (int64, error)
Delete(ctx context.Context, orgId int64, uid string) (int64, error)
Delete(ctx context.Context, uid string) (int64, error)
GetOrgIdByAccessToken(ctx context.Context, accessToken string) (int64, error)
FindByDashboardFolder(ctx context.Context, dashboard *dashboards.Dashboard) ([]*PublicDashboard, error)
ExistsEnabledByAccessToken(ctx context.Context, accessToken string) (bool, error)
ExistsEnabledByDashboardUid(ctx context.Context, dashboardUid string) (bool, error)
}

View File

@ -329,17 +329,37 @@ func (pd *PublicDashboardServiceImpl) GetOrgIdByAccessToken(ctx context.Context,
return pd.store.GetOrgIdByAccessToken(ctx, accessToken)
}
func (pd *PublicDashboardServiceImpl) Delete(ctx context.Context, orgId int64, uid string) error {
affectedRows, err := pd.store.Delete(ctx, orgId, uid)
func (pd *PublicDashboardServiceImpl) Delete(ctx context.Context, uid string) error {
return pd.serviceWrapper.Delete(ctx, uid)
}
func (pd *PublicDashboardServiceImpl) DeleteByDashboard(ctx context.Context, dashboard *dashboards.Dashboard) error {
if dashboard.IsFolder {
// get all pubdashes for the folder
pubdashes, err := pd.store.FindByDashboardFolder(ctx, dashboard)
if err != nil {
return err
}
// delete each pubdash
for _, pubdash := range pubdashes {
err = pd.serviceWrapper.Delete(ctx, pubdash.Uid)
if err != nil {
return err
}
}
return nil
}
pubdash, err := pd.store.FindByDashboardUid(ctx, dashboard.OrgID, dashboard.UID)
if err != nil {
return ErrInternalServerError.Errorf("Delete: failed to delete a public dashboard by orgId: %d and Uid: %s %w", orgId, uid, err)
return ErrInternalServerError.Errorf("DeleteByDashboard: error finding a public dashboard by dashboard orgId: %d and Uid: %s %w", dashboard.OrgID, dashboard.UID, err)
}
if pubdash == nil {
return nil
}
if affectedRows == 0 {
return ErrPublicDashboardNotFound.Errorf("Delete: Public dashboard not found by orgId: %d and Uid: %s", orgId, uid)
}
return nil
return pd.serviceWrapper.Delete(ctx, pubdash.Uid)
}
// intervalMS and maxQueryData values are being calculated on the frontend for regular dashboards

View File

@ -498,13 +498,13 @@ func TestDeletePublicDashboard(t *testing.T) {
{
Name: "Public dashboard not found",
AffectedRowsResp: 0,
ExpectedErrResp: ErrPublicDashboardNotFound.Errorf("Delete: Public dashboard not found by orgId: 13 and Uid: uid"),
ExpectedErrResp: nil,
StoreRespErr: nil,
},
{
Name: "Database error",
AffectedRowsResp: 0,
ExpectedErrResp: ErrInternalServerError.Errorf("Delete: failed to delete a public dashboard by orgId: 13 and Uid: uid db error!"),
ExpectedErrResp: ErrInternalServerError.Errorf("Delete: failed to delete a public dashboard by Uid: uid db error!"),
StoreRespErr: errors.New("db error!"),
},
}
@ -512,14 +512,18 @@ func TestDeletePublicDashboard(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.Name, func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.AffectedRowsResp, tt.StoreRespErr)
service := &PublicDashboardServiceImpl{
store.On("Delete", mock.Anything, mock.Anything).Return(tt.AffectedRowsResp, tt.StoreRespErr)
serviceWrapper := &PublicDashboardServiceWrapperImpl{
log: log.New("test.logger"),
store: store,
}
service := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: store,
serviceWrapper: serviceWrapper,
}
err := service.Delete(context.Background(), 13, "uid")
err := service.Delete(context.Background(), "uid")
if tt.ExpectedErrResp != nil {
assert.Equal(t, tt.ExpectedErrResp.Error(), err.Error())
assert.Equal(t, tt.ExpectedErrResp.Error(), err.Error())
@ -966,6 +970,56 @@ func TestPublicDashboardServiceImpl_NewPublicDashboardAccessToken(t *testing.T)
}
}
func TestDeleteByDashboard(t *testing.T) {
t.Run("will return nil when pubdash not found", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: false}
store.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
assert.Nil(t, err)
})
t.Run("will delete pubdash when dashboard deleted", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: false}
pubdash := &PublicDashboard{Uid: "2", OrgId: 1, DashboardUid: dashboard.UID}
store.On("FindByDashboardUid", mock.Anything, mock.Anything, mock.Anything).Return(pubdash, nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
require.NoError(t, err)
})
t.Run("will delete pubdashes when dashboard folder deleted", func(t *testing.T) {
store := NewFakePublicDashboardStore(t)
pd := &PublicDashboardServiceImpl{store: store, serviceWrapper: ProvideServiceWrapper(store)}
dashboard := &dashboards.Dashboard{UID: "1", OrgID: 1, IsFolder: true}
pubdash1 := &PublicDashboard{Uid: "2", OrgId: 1, DashboardUid: dashboard.UID}
pubdash2 := &PublicDashboard{Uid: "3", OrgId: 1, DashboardUid: dashboard.UID}
store.On("FindByDashboardFolder", mock.Anything, mock.Anything).Return([]*PublicDashboard{pubdash1, pubdash2}, nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
store.On("Delete", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
err := pd.DeleteByDashboard(context.Background(), dashboard)
require.NoError(t, err)
})
}
func TestGenerateAccessToken(t *testing.T) {
accessToken, err := GenerateAccessToken()
t.Run("length", func(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 32, len(accessToken))
})
t.Run("no - ", func(t *testing.T) {
assert.False(t, strings.Contains("-", accessToken))
})
}
func CreateDatasource(dsType string, uid string) struct {
Type *string `json:"type,omitempty"`
Uid *string `json:"uid,omitempty"`
@ -1070,16 +1124,3 @@ func insertTestDashboard(t *testing.T, dashboardStore dashboards.Store, title st
dash.Data.Set("uid", dash.UID)
return dash
}
func TestGenerateAccessToken(t *testing.T) {
accessToken, err := GenerateAccessToken()
t.Run("length", func(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 32, len(accessToken))
})
t.Run("no - ", func(t *testing.T) {
assert.False(t, strings.Contains("-", accessToken))
})
}

View File

@ -43,3 +43,12 @@ func (pd *PublicDashboardServiceWrapperImpl) FindByDashboardUid(ctx context.Cont
return pubdash, nil
}
func (pd *PublicDashboardServiceWrapperImpl) Delete(ctx context.Context, uid string) error {
_, err := pd.store.Delete(ctx, uid)
if err != nil {
return ErrInternalServerError.Errorf("Delete: failed to delete a public dashboard by Uid: %s %w", uid, err)
}
return nil
}