public-dashboards: Add log statement when public dashboard enabled or disabled (#54133)

* refactor apis for consistent outputs
* add dashboardUid validation at API layer
* add check for empty dashboardUid on SavePublicDashboard
* remove public dashboard errors from models package.

Co-authored-by: Ezequiel Victorero <evictorero@gmail.com>
This commit is contained in:
Jeff Levin 2022-08-26 11:28:54 -08:00 committed by GitHub
parent b9be2815d3
commit 5cb9fca990
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 215 additions and 148 deletions

View File

@ -125,28 +125,6 @@ var (
StatusCode: 404, StatusCode: 404,
Status: "not-found", Status: "not-found",
} }
ErrPublicDashboardFailedGenerateUniqueUid = DashboardErr{
Reason: "Failed to generate unique public dashboard id",
StatusCode: 500,
}
ErrPublicDashboardFailedGenerateAccesstoken = DashboardErr{
Reason: "Failed to public dashboard access token",
StatusCode: 500,
}
ErrPublicDashboardNotFound = DashboardErr{
Reason: "Public dashboard not found",
StatusCode: 404,
Status: "not-found",
}
ErrPublicDashboardPanelNotFound = DashboardErr{
Reason: "Panel not found in dashboard",
StatusCode: 404,
Status: "not-found",
}
ErrPublicDashboardIdentifierNotSet = DashboardErr{
Reason: "No Uid for public dashboard specified",
StatusCode: 400,
}
ErrFolderNotFound = errors.New("folder not found") ErrFolderNotFound = errors.New("folder not found")
ErrFolderVersionMismatch = errors.New("the folder has been changed by someone else") ErrFolderVersionMismatch = errors.New("the folder has been changed by someone else")

View File

@ -120,23 +120,28 @@ func (api *Api) GetPublicDashboardConfig(c *models.ReqContext) response.Response
// Sets public dashboard configuration for dashboard // Sets public dashboard configuration for dashboard
// POST /api/dashboards/uid/:uid/public-config // POST /api/dashboards/uid/:uid/public-config
func (api *Api) SavePublicDashboardConfig(c *models.ReqContext) response.Response { func (api *Api) SavePublicDashboardConfig(c *models.ReqContext) response.Response {
// exit if we don't have a valid dashboardUid
dashboardUid := web.Params(c.Req)[":uid"]
if dashboardUid == "" || !util.IsValidShortUID(dashboardUid) {
handleDashboardErr(http.StatusBadRequest, "no dashboardUid", dashboards.ErrDashboardIdentifierNotSet)
}
pubdash := &PublicDashboard{} pubdash := &PublicDashboard{}
if err := web.Bind(c.Req, pubdash); err != nil { if err := web.Bind(c.Req, pubdash); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err) return response.Error(http.StatusBadRequest, "bad request data", err)
} }
// Always set the org id to the current auth session orgId // Always set the orgID and userID from the session
pubdash.OrgId = c.OrgID pubdash.OrgId = c.OrgID
dto := SavePublicDashboardConfigDTO{ dto := SavePublicDashboardConfigDTO{
OrgId: c.OrgID,
DashboardUid: web.Params(c.Req)[":uid"],
UserId: c.UserID, UserId: c.UserID,
OrgId: c.OrgID,
DashboardUid: dashboardUid,
PublicDashboard: pubdash, PublicDashboard: pubdash,
} }
// Save the public dashboard // Save the public dashboard
pubdash, err := api.PublicDashboardService.SavePublicDashboardConfig(c.Req.Context(), &dto) pubdash, err := api.PublicDashboardService.SavePublicDashboardConfig(c.Req.Context(), c.SignedInUser, &dto)
if err != nil { if err != nil {
return handleDashboardErr(http.StatusInternalServerError, "Failed to save public dashboard configuration", err) return handleDashboardErr(http.StatusInternalServerError, "Failed to save public dashboard configuration", err)
} }

View File

@ -240,7 +240,7 @@ func TestApiSavePublicDashboardConfig(t *testing.T) {
for _, test := range testCases { for _, test := range testCases {
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
service := publicdashboards.NewFakePublicDashboardService(t) service := publicdashboards.NewFakePublicDashboardService(t)
service.On("SavePublicDashboardConfig", mock.Anything, mock.AnythingOfType("*models.SavePublicDashboardConfigDTO")). service.On("SavePublicDashboardConfig", mock.Anything, mock.Anything, mock.AnythingOfType("*models.SavePublicDashboardConfigDTO")).
Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr) Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr)
cfg := setting.NewCfg() cfg := setting.NewCfg()
@ -558,7 +558,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.RBACEnabled = false cfg.RBACEnabled = false
service := publicdashboardsService.ProvideService(cfg, store) service := publicdashboardsService.ProvideService(cfg, store)
pubdash, err := service.SavePublicDashboardConfig(context.Background(), savePubDashboardCmd) pubdash, err := service.SavePublicDashboardConfig(context.Background(), &user.SignedInUser{}, savePubDashboardCmd)
require.NoError(t, err) require.NoError(t, err)
// setup test server // setup test server

View File

@ -113,6 +113,31 @@ func (d *PublicDashboardStoreImpl) GenerateNewPublicDashboardUid(ctx context.Con
return uid, nil return uid, nil
} }
// Retrieves public dashboard configuration by Uid
func (d *PublicDashboardStoreImpl) GetPublicDashboardByUid(ctx context.Context, uid string) (*PublicDashboard, error) {
if uid == "" {
return nil, nil
}
var found bool
pdRes := &PublicDashboard{Uid: uid}
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
var err error
found, err = sess.Get(pdRes)
return err
})
if err != nil {
return nil, err
}
if !found {
return nil, nil
}
return pdRes, err
}
// Retrieves public dashboard configuration // Retrieves public dashboard configuration
func (d *PublicDashboardStoreImpl) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) { func (d *PublicDashboardStoreImpl) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) {
if dashboardUid == "" { if dashboardUid == "" {
@ -138,7 +163,11 @@ func (d *PublicDashboardStoreImpl) GetPublicDashboardConfig(ctx context.Context,
} }
// Persists public dashboard configuration // Persists public dashboard configuration
func (d *PublicDashboardStoreImpl) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) (*PublicDashboard, error) { func (d *PublicDashboardStoreImpl) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error {
if cmd.PublicDashboard.DashboardUid == "" {
return dashboards.ErrDashboardIdentifierNotSet
}
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
_, err := sess.UseBool("is_enabled").Insert(&cmd.PublicDashboard) _, err := sess.UseBool("is_enabled").Insert(&cmd.PublicDashboard)
if err != nil { if err != nil {
@ -148,11 +177,7 @@ func (d *PublicDashboardStoreImpl) SavePublicDashboardConfig(ctx context.Context
return nil return nil
}) })
if err != nil { return err
return nil, err
}
return &cmd.PublicDashboard, nil
} }
// updates existing public dashboard configuration // updates existing public dashboard configuration

View File

@ -67,7 +67,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
t.Run("AccessTokenExists will return true when at least one public dashboard has a matching access token", func(t *testing.T) { t.Run("AccessTokenExists will return true when at least one public dashboard has a matching access token", func(t *testing.T) {
setup() setup()
_, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "abc123", Uid: "abc123",
@ -98,7 +98,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
t.Run("PublicDashboardEnabled Will return true when dashboard has at least one enabled public dashboard", func(t *testing.T) { t.Run("PublicDashboardEnabled Will return true when dashboard has at least one enabled public dashboard", func(t *testing.T) {
setup() setup()
_, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "abc123", Uid: "abc123",
@ -120,7 +120,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
t.Run("PublicDashboardEnabled will return false when dashboard has public dashboards but they are not enabled", func(t *testing.T) { t.Run("PublicDashboardEnabled will return false when dashboard has public dashboards but they are not enabled", func(t *testing.T) {
setup() setup()
_, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: false, IsEnabled: false,
Uid: "abc123", Uid: "abc123",
@ -141,7 +141,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
t.Run("returns PublicDashboard and Dashboard", func(t *testing.T) { t.Run("returns PublicDashboard and Dashboard", func(t *testing.T) {
setup() setup()
pubdash, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ cmd := SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "abc1234", Uid: "abc1234",
@ -152,14 +152,16 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
CreatedBy: 7, CreatedBy: 7,
AccessToken: "NOTAREALUUID", AccessToken: "NOTAREALUUID",
}, },
}) }
err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)
pd, d, err := publicdashboardStore.GetPublicDashboard(context.Background(), "NOTAREALUUID") pd, d, err := publicdashboardStore.GetPublicDashboard(context.Background(), "NOTAREALUUID")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, pd, pubdash) assert.Equal(t, pd, &cmd.PublicDashboard)
assert.Equal(t, d.Uid, pubdash.DashboardUid) assert.Equal(t, d.Uid, cmd.PublicDashboard.DashboardUid)
}) })
t.Run("returns ErrPublicDashboardNotFound with empty uid", func(t *testing.T) { t.Run("returns ErrPublicDashboardNotFound with empty uid", func(t *testing.T) {
@ -176,7 +178,7 @@ func TestIntegrationGetPublicDashboard(t *testing.T) {
t.Run("returns ErrDashboardNotFound when Dashboard not found", func(t *testing.T) { t.Run("returns ErrDashboardNotFound when Dashboard not found", func(t *testing.T) {
setup() setup()
_, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "abc1234", Uid: "abc1234",
@ -219,10 +221,9 @@ func TestIntegrationGetPublicDashboardConfig(t *testing.T) {
require.Error(t, dashboards.ErrDashboardIdentifierNotSet, err) require.Error(t, dashboards.ErrDashboardIdentifierNotSet, err)
}) })
t.Run("returns isPublic along with public dashboard when exists", func(t *testing.T) { t.Run("returns along with public dashboard when exists", func(t *testing.T) {
setup() setup()
// insert test public dashboard cmd := SavePublicDashboardConfigCommand{
resp, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "pubdash-uid", Uid: "pubdash-uid",
@ -232,14 +233,17 @@ func TestIntegrationGetPublicDashboardConfig(t *testing.T) {
CreatedAt: DefaultTime, CreatedAt: DefaultTime,
CreatedBy: 7, CreatedBy: 7,
}, },
}) }
// insert test public dashboard
err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), cmd)
require.NoError(t, err) require.NoError(t, err)
// retrieve from db
pubdash, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, savedDashboard.Uid) pubdash, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, savedDashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, assert.ObjectsAreEqualValues(resp, pubdash)) assert.True(t, assert.ObjectsAreEqualValues(&cmd.PublicDashboard, pubdash))
assert.True(t, assert.ObjectsAreEqual(resp, pubdash))
}) })
} }
@ -261,7 +265,7 @@ func TestIntegrationSavePublicDashboardConfig(t *testing.T) {
t.Run("saves new public dashboard", func(t *testing.T) { t.Run("saves new public dashboard", func(t *testing.T) {
setup() setup()
resp, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
IsEnabled: true, IsEnabled: true,
Uid: "pubdash-uid", Uid: "pubdash-uid",
@ -278,9 +282,6 @@ func TestIntegrationSavePublicDashboardConfig(t *testing.T) {
pubdash, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, savedDashboard.Uid) pubdash, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), savedDashboard.OrgId, savedDashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
//verify saved response and queried response are the same
assert.Equal(t, resp, pubdash)
// verify we have a valid uid // verify we have a valid uid
assert.True(t, util.IsValidShortUID(pubdash.Uid)) assert.True(t, util.IsValidShortUID(pubdash.Uid))
@ -289,6 +290,23 @@ func TestIntegrationSavePublicDashboardConfig(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.False(t, pubdash2.IsEnabled) assert.False(t, pubdash2.IsEnabled)
}) })
t.Run("guards from saving without dashboardUid", func(t *testing.T) {
setup()
err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{
IsEnabled: true,
Uid: "pubdash-uid",
DashboardUid: "",
OrgId: savedDashboard.OrgId,
TimeSettings: DefaultTimeSettings,
CreatedAt: DefaultTime,
CreatedBy: 7,
AccessToken: "NOTAREALUUID",
},
})
assert.Error(t, err, dashboards.ErrDashboardIdentifierNotSet)
})
} }
func TestIntegrationUpdatePublicDashboard(t *testing.T) { func TestIntegrationUpdatePublicDashboard(t *testing.T) {
@ -310,7 +328,7 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
setup() setup()
pdUid := "asdf1234" pdUid := "asdf1234"
_, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
Uid: pdUid, Uid: pdUid,
DashboardUid: savedDashboard.Uid, DashboardUid: savedDashboard.Uid,
@ -325,7 +343,7 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
// inserting two different public dashboards to test update works and only affect the desired pd by uid // inserting two different public dashboards to test update works and only affect the desired pd by uid
anotherPdUid := "anotherUid" anotherPdUid := "anotherUid"
_, err = publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ err = publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
Uid: anotherPdUid, Uid: anotherPdUid,
DashboardUid: anotherSavedDashboard.Uid, DashboardUid: anotherSavedDashboard.Uid,
@ -365,11 +383,11 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
// not updated dashboard shouldn't have changed // not updated dashboard shouldn't have changed
pdNotUpdatedRetrieved, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), anotherSavedDashboard.OrgId, anotherSavedDashboard.Uid) pdNotUpdatedRetrieved, err := publicdashboardStore.GetPublicDashboardConfig(context.Background(), anotherSavedDashboard.OrgId, anotherSavedDashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
assert.NotEqual(t, updatedPublicDashboard.UpdatedAt, pdNotUpdatedRetrieved.UpdatedAt) assert.NotEqual(t, updatedPublicDashboard.UpdatedAt, pdNotUpdatedRetrieved.UpdatedAt)
assert.NotEqual(t, updatedPublicDashboard.IsEnabled, pdNotUpdatedRetrieved.IsEnabled) assert.NotEqual(t, updatedPublicDashboard.IsEnabled, pdNotUpdatedRetrieved.IsEnabled)
}) })
} }
func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardStore, title string, orgId int64, func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardStore, title string, orgId int64,
folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard { folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
t.Helper() t.Helper()

View File

@ -186,13 +186,13 @@ func (_m *FakePublicDashboardService) PublicDashboardEnabled(ctx context.Context
return r0, r1 return r0, r1
} }
// SavePublicDashboardConfig provides a mock function with given fields: ctx, dto // SavePublicDashboardConfig provides a mock function with given fields: ctx, u, dto
func (_m *FakePublicDashboardService) SavePublicDashboardConfig(ctx context.Context, dto *publicdashboardsmodels.SavePublicDashboardConfigDTO) (*publicdashboardsmodels.PublicDashboard, error) { func (_m *FakePublicDashboardService) SavePublicDashboardConfig(ctx context.Context, u *user.SignedInUser, dto *publicdashboardsmodels.SavePublicDashboardConfigDTO) (*publicdashboardsmodels.PublicDashboard, error) {
ret := _m.Called(ctx, dto) ret := _m.Called(ctx, u, dto)
var r0 *publicdashboardsmodels.PublicDashboard var r0 *publicdashboardsmodels.PublicDashboard
if rf, ok := ret.Get(0).(func(context.Context, *publicdashboardsmodels.SavePublicDashboardConfigDTO) *publicdashboardsmodels.PublicDashboard); ok { if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, *publicdashboardsmodels.SavePublicDashboardConfigDTO) *publicdashboardsmodels.PublicDashboard); ok {
r0 = rf(ctx, dto) r0 = rf(ctx, u, dto)
} else { } else {
if ret.Get(0) != nil { if ret.Get(0) != nil {
r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard) r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
@ -200,8 +200,8 @@ func (_m *FakePublicDashboardService) SavePublicDashboardConfig(ctx context.Cont
} }
var r1 error var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *publicdashboardsmodels.SavePublicDashboardConfigDTO) error); ok { if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, *publicdashboardsmodels.SavePublicDashboardConfigDTO) error); ok {
r1 = rf(ctx, dto) r1 = rf(ctx, u, dto)
} else { } else {
r1 = ret.Error(1) r1 = ret.Error(1)
} }

View File

@ -115,6 +115,29 @@ func (_m *FakePublicDashboardStore) GetPublicDashboard(ctx context.Context, acce
return r0, r1, r2 return r0, r1, r2
} }
// GetPublicDashboardByUid provides a mock function with given fields: ctx, uid
func (_m *FakePublicDashboardStore) GetPublicDashboardByUid(ctx context.Context, uid string) (*publicdashboardsmodels.PublicDashboard, error) {
ret := _m.Called(ctx, uid)
var r0 *publicdashboardsmodels.PublicDashboard
if rf, ok := ret.Get(0).(func(context.Context, string) *publicdashboardsmodels.PublicDashboard); ok {
r0 = rf(ctx, uid)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, uid)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetPublicDashboardConfig provides a mock function with given fields: ctx, orgId, dashboardUid // GetPublicDashboardConfig provides a mock function with given fields: ctx, orgId, dashboardUid
func (_m *FakePublicDashboardStore) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*publicdashboardsmodels.PublicDashboard, error) { func (_m *FakePublicDashboardStore) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*publicdashboardsmodels.PublicDashboard, error) {
ret := _m.Called(ctx, orgId, dashboardUid) ret := _m.Called(ctx, orgId, dashboardUid)
@ -160,26 +183,17 @@ func (_m *FakePublicDashboardStore) PublicDashboardEnabled(ctx context.Context,
} }
// SavePublicDashboardConfig provides a mock function with given fields: ctx, cmd // SavePublicDashboardConfig provides a mock function with given fields: ctx, cmd
func (_m *FakePublicDashboardStore) SavePublicDashboardConfig(ctx context.Context, cmd publicdashboardsmodels.SavePublicDashboardConfigCommand) (*publicdashboardsmodels.PublicDashboard, error) { func (_m *FakePublicDashboardStore) SavePublicDashboardConfig(ctx context.Context, cmd publicdashboardsmodels.SavePublicDashboardConfigCommand) error {
ret := _m.Called(ctx, cmd) ret := _m.Called(ctx, cmd)
var r0 *publicdashboardsmodels.PublicDashboard var r0 error
if rf, ok := ret.Get(0).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) *publicdashboardsmodels.PublicDashboard); ok { if rf, ok := ret.Get(0).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) error); ok {
r0 = rf(ctx, cmd) r0 = rf(ctx, cmd)
} else { } else {
if ret.Get(0) != nil { r0 = ret.Error(0)
r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
}
} }
var r1 error return r0
if rf, ok := ret.Get(1).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) error); ok {
r1 = rf(ctx, cmd)
} else {
r1 = ret.Error(1)
}
return r0, r1
} }
// UpdatePublicDashboardConfig provides a mock function with given fields: ctx, cmd // UpdatePublicDashboardConfig provides a mock function with given fields: ctx, cmd

View File

@ -17,7 +17,7 @@ type Service interface {
GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error)
GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error)
GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) SavePublicDashboardConfig(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error)
BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64) (dtos.MetricRequest, error) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64) (dtos.MetricRequest, error)
PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error)
AccessTokenExists(ctx context.Context, accessToken string) (bool, error) AccessTokenExists(ctx context.Context, accessToken string) (bool, error)
@ -28,8 +28,9 @@ type Store interface {
GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error)
GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error)
GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
GetPublicDashboardByUid(ctx context.Context, uid string) (*PublicDashboard, error)
GenerateNewPublicDashboardUid(ctx context.Context) (string, error) GenerateNewPublicDashboardUid(ctx context.Context) (string, error)
SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) (*PublicDashboard, error) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error
UpdatePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error UpdatePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error
PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error)
AccessTokenExists(ctx context.Context, accessToken string) (bool, error) AccessTokenExists(ctx context.Context, accessToken string) (bool, error)

View File

@ -86,11 +86,12 @@ func (pd *PublicDashboardServiceImpl) GetPublicDashboardConfig(ctx context.Conte
// SavePublicDashboardConfig is a helper method to persist the sharing config // SavePublicDashboardConfig is a helper method to persist the sharing config
// to the database. It handles validations for sharing config and persistence // to the database. It handles validations for sharing config and persistence
func (pd *PublicDashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) { func (pd *PublicDashboardServiceImpl) SavePublicDashboardConfig(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) {
dashboard, err := pd.GetDashboard(ctx, dto.DashboardUid) dashboard, err := pd.GetDashboard(ctx, dto.DashboardUid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = validation.ValidateSavePublicDashboard(dto, dashboard) err = validation.ValidateSavePublicDashboard(dto, dashboard)
if err != nil { if err != nil {
return nil, err return nil, err
@ -101,22 +102,45 @@ func (pd *PublicDashboardServiceImpl) SavePublicDashboardConfig(ctx context.Cont
dto.PublicDashboard.TimeSettings = simplejson.New() dto.PublicDashboard.TimeSettings = simplejson.New()
} }
if dto.PublicDashboard.Uid == "" { // get existing public dashboard if exists
return pd.savePublicDashboardConfig(ctx, dto) existingPubdash, err := pd.store.GetPublicDashboardByUid(ctx, dto.PublicDashboard.Uid)
}
return pd.updatePublicDashboardConfig(ctx, dto)
}
func (pd *PublicDashboardServiceImpl) savePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) {
uid, err := pd.store.GenerateNewPublicDashboardUid(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// save changes
var pubdashUid string
if existingPubdash == nil {
pubdashUid, err = pd.savePublicDashboardConfig(ctx, dto)
} else {
pubdashUid, err = pd.updatePublicDashboardConfig(ctx, dto)
}
if err != nil {
return nil, err
}
//Get latest public dashboard to return
newPubdash, err := pd.store.GetPublicDashboardByUid(ctx, pubdashUid)
if err != nil {
return nil, err
}
pd.logIsEnabledChanged(existingPubdash, newPubdash, u)
return newPubdash, err
}
// Called by SavePublicDashboardConfig this handles business logic
// to generate token and calls create at the database layer
func (pd *PublicDashboardServiceImpl) savePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (string, error) {
uid, err := pd.store.GenerateNewPublicDashboardUid(ctx)
if err != nil {
return "", err
}
accessToken, err := GenerateAccessToken() accessToken, err := GenerateAccessToken()
if err != nil { if err != nil {
return nil, err return "", err
} }
cmd := SavePublicDashboardConfigCommand{ cmd := SavePublicDashboardConfigCommand{
@ -132,10 +156,17 @@ func (pd *PublicDashboardServiceImpl) savePublicDashboardConfig(ctx context.Cont
}, },
} }
return pd.store.SavePublicDashboardConfig(ctx, cmd) err = pd.store.SavePublicDashboardConfig(ctx, cmd)
if err != nil {
return "", err
}
return uid, nil
} }
func (pd *PublicDashboardServiceImpl) updatePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) { // Called by SavePublicDashboard this handles business logic for updating a
// dashboard and calls update at the database layer
func (pd *PublicDashboardServiceImpl) updatePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (string, error) {
cmd := SavePublicDashboardConfigCommand{ cmd := SavePublicDashboardConfigCommand{
PublicDashboard: PublicDashboard{ PublicDashboard: PublicDashboard{
Uid: dto.PublicDashboard.Uid, Uid: dto.PublicDashboard.Uid,
@ -146,17 +177,7 @@ func (pd *PublicDashboardServiceImpl) updatePublicDashboardConfig(ctx context.Co
}, },
} }
err := pd.store.UpdatePublicDashboardConfig(ctx, cmd) return dto.PublicDashboard.Uid, pd.store.UpdatePublicDashboardConfig(ctx, cmd)
if err != nil {
return nil, err
}
publicDashboard, err := pd.store.GetPublicDashboardConfig(ctx, dto.OrgId, dto.DashboardUid)
if err != nil {
return nil, err
}
return publicDashboard, nil
} }
// BuildPublicDashboardMetricRequest merges public dashboard parameters with // BuildPublicDashboardMetricRequest merges public dashboard parameters with
@ -218,3 +239,23 @@ func GenerateAccessToken() (string, error) {
return fmt.Sprintf("%x", token[:]), nil return fmt.Sprintf("%x", token[:]), nil
} }
// Log when PublicDashboard.IsEnabled changed
func (pd *PublicDashboardServiceImpl) logIsEnabledChanged(existingPubdash *PublicDashboard, newPubdash *PublicDashboard, u *user.SignedInUser) {
if publicDashboardIsEnabledChanged(existingPubdash, newPubdash) {
verb := "disabled"
if newPubdash.IsEnabled {
verb = "enabled"
}
pd.log.Info(fmt.Sprintf("Public dashboard %v: dashboardUid: %v, user:%v", verb, newPubdash.Uid, u.Login))
}
}
// Checks to see if PublicDashboard.Isenabled is true on create or changed on update
func publicDashboardIsEnabledChanged(existingPubdash *PublicDashboard, newPubdash *PublicDashboard) bool {
// creating dashboard, enabled true
newDashCreated := existingPubdash == nil && newPubdash.IsEnabled
// updating dashboard, enabled changed
isEnabledChanged := existingPubdash != nil && newPubdash.IsEnabled != existingPubdash.IsEnabled
return newDashCreated || isEnabledChanged
}

View File

@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/user"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -25,6 +26,7 @@ import (
var timeSettings, _ = simplejson.NewJson([]byte(`{"from": "now-12", "to": "now"}`)) var timeSettings, _ = simplejson.NewJson([]byte(`{"from": "now-12", "to": "now"}`))
var defaultPubdashTimeSettings, _ = simplejson.NewJson([]byte(`{}`)) var defaultPubdashTimeSettings, _ = simplejson.NewJson([]byte(`{}`))
var dashboardData = simplejson.NewFromAny(map[string]interface{}{"time": map[string]interface{}{"from": "now-8", "to": "now"}}) var dashboardData = simplejson.NewFromAny(map[string]interface{}{"time": map[string]interface{}{"from": "now-8", "to": "now"}})
var SignedInUser = &user.SignedInUser{UserID: 1234, Login: "user@login.com"}
func TestLogPrefix(t *testing.T) { func TestLogPrefix(t *testing.T) {
assert.Equal(t, LogPrefix, "publicdashboards.service") assert.Equal(t, LogPrefix, "publicdashboards.service")
@ -134,7 +136,7 @@ func TestSavePublicDashboard(t *testing.T) {
}, },
} }
_, err := service.SavePublicDashboardConfig(context.Background(), dto) _, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err) require.NoError(t, err)
pubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid) pubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
@ -177,7 +179,7 @@ func TestSavePublicDashboard(t *testing.T) {
}, },
} }
_, err := service.SavePublicDashboardConfig(context.Background(), dto) _, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err) require.NoError(t, err)
pubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid) pubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
@ -208,7 +210,7 @@ func TestSavePublicDashboard(t *testing.T) {
}, },
} }
_, err := service.SavePublicDashboardConfig(context.Background(), dto) _, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.Error(t, err) require.Error(t, err)
}) })
} }
@ -235,10 +237,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
}, },
} }
_, err := service.SavePublicDashboardConfig(context.Background(), dto) savedPubdash, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err)
savedPubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
// attempt to overwrite settings // attempt to overwrite settings
@ -261,10 +260,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
// Since the dto.PublicDashboard has a uid, this will call // Since the dto.PublicDashboard has a uid, this will call
// service.updatePublicDashboardConfig // service.updatePublicDashboardConfig
_, err = service.SavePublicDashboardConfig(context.Background(), dto) updatedPubdash, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err)
updatedPubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
// don't get updated // don't get updated
@ -304,10 +300,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
// Since the dto.PublicDashboard has a uid, this will call // Since the dto.PublicDashboard has a uid, this will call
// service.updatePublicDashboardConfig // service.updatePublicDashboardConfig
_, err := service.SavePublicDashboardConfig(context.Background(), dto) savedPubdash, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err)
savedPubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
// attempt to overwrite settings // attempt to overwrite settings
@ -327,10 +320,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
}, },
} }
_, err = service.SavePublicDashboardConfig(context.Background(), dto) updatedPubdash, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err)
updatedPubdash, err := service.GetPublicDashboardConfig(context.Background(), dashboard.OrgId, dashboard.Uid)
require.NoError(t, err) require.NoError(t, err)
timeSettings, err := simplejson.NewJson([]byte("{}")) timeSettings, err := simplejson.NewJson([]byte("{}"))
@ -385,7 +375,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
}, },
} }
publicDashboardPD, err := service.SavePublicDashboardConfig(context.Background(), dto) publicDashboardPD, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, dto)
require.NoError(t, err) require.NoError(t, err)
nonPublicDto := &SavePublicDashboardConfigDTO{ nonPublicDto := &SavePublicDashboardConfigDTO{
@ -399,7 +389,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
}, },
} }
nonPublicDashboardPD, err := service.SavePublicDashboardConfig(context.Background(), nonPublicDto) nonPublicDashboardPD, err := service.SavePublicDashboardConfig(context.Background(), SignedInUser, nonPublicDto)
require.NoError(t, err) require.NoError(t, err)
t.Run("extracts queries from provided dashboard", func(t *testing.T) { t.Run("extracts queries from provided dashboard", func(t *testing.T) {
@ -522,3 +512,21 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto
dash.Data.Set("uid", dash.Uid) dash.Data.Set("uid", dash.Uid)
return dash return dash
} }
func TestDashboardEnabledChanged(t *testing.T) {
t.Run("created isEnabled: false", func(t *testing.T) {
assert.False(t, publicDashboardIsEnabledChanged(nil, &PublicDashboard{IsEnabled: false}))
})
t.Run("created isEnabled: true", func(t *testing.T) {
assert.True(t, publicDashboardIsEnabledChanged(nil, &PublicDashboard{IsEnabled: true}))
})
t.Run("updated isEnabled same", func(t *testing.T) {
assert.False(t, publicDashboardIsEnabledChanged(&PublicDashboard{IsEnabled: true}, &PublicDashboard{IsEnabled: true}))
})
t.Run("updated isEnabled changed", func(t *testing.T) {
assert.True(t, publicDashboardIsEnabledChanged(&PublicDashboard{IsEnabled: false}, &PublicDashboard{IsEnabled: true}))
})
}

View File

@ -10,22 +10,6 @@ import (
) )
func TestValidateSavePublicDashboard(t *testing.T) { func TestValidateSavePublicDashboard(t *testing.T) {
t.Run("Returns validation error when dto has no dashboard uid", func(t *testing.T) {
dashboard := models.NewDashboard("dashboardTitle")
dto := &publicdashboardModels.SavePublicDashboardConfigDTO{DashboardUid: "", OrgId: 1, UserId: 1, PublicDashboard: nil}
err := ValidateSavePublicDashboard(dto, dashboard)
require.ErrorContains(t, err, "Unique identifier needed to be able to get a dashboard")
})
t.Run("Returns no validation error when dto has dashboard uid", func(t *testing.T) {
dashboard := models.NewDashboard("dashboardTitle")
dto := &publicdashboardModels.SavePublicDashboardConfigDTO{DashboardUid: "abc123", OrgId: 1, UserId: 1, PublicDashboard: nil}
err := ValidateSavePublicDashboard(dto, dashboard)
require.NoError(t, err)
})
t.Run("Returns validation error when dashboard has template variables", func(t *testing.T) { t.Run("Returns validation error when dashboard has template variables", func(t *testing.T) {
templateVars := []byte(`{ templateVars := []byte(`{
"templating": { "templating": {

View File

@ -2,22 +2,15 @@ package validation
import ( import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models" publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
) )
func ValidateSavePublicDashboard(dto *publicDashboardModels.SavePublicDashboardConfigDTO, dashboard *models.Dashboard) error { func ValidateSavePublicDashboard(dto *publicDashboardModels.SavePublicDashboardConfigDTO, dashboard *models.Dashboard) error {
var err error
if len(dto.DashboardUid) == 0 {
return dashboards.ErrDashboardIdentifierNotSet
}
if hasTemplateVariables(dashboard) { if hasTemplateVariables(dashboard) {
return publicDashboardModels.ErrPublicDashboardHasTemplateVariables return publicDashboardModels.ErrPublicDashboardHasTemplateVariables
} }
return err return nil
} }
func hasTemplateVariables(dashboard *models.Dashboard) bool { func hasTemplateVariables(dashboard *models.Dashboard) bool {