mirror of https://github.com/grafana/grafana.git
				
				
				
			Public Dashboards: Adds template variable validation for pubdash on the backend (#52566)
Validates template variables for pubdash on the backend when saving a public dashboard
This commit is contained in:
		
							parent
							
								
									9ef29bb5c3
								
							
						
					
					
						commit
						cf86c696e0
					
				| 
						 | 
					@ -126,6 +126,7 @@ func (api *Api) SavePublicDashboardConfig(c *models.ReqContext) response.Respons
 | 
				
			||||||
		PublicDashboard: pubdash,
 | 
							PublicDashboard: pubdash,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Save the public dashboard
 | 
				
			||||||
	pubdash, err := api.PublicDashboardService.SavePublicDashboardConfig(c.Req.Context(), &dto)
 | 
						pubdash, err := api.PublicDashboardService.SavePublicDashboardConfig(c.Req.Context(), &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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,22 @@ func ProvideStore(sqlStore *sqlstore.SQLStore) *PublicDashboardStoreImpl {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *PublicDashboardStoreImpl) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
 | 
				
			||||||
 | 
						dashboard := &models.Dashboard{Uid: dashboardUid}
 | 
				
			||||||
 | 
						err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
 | 
				
			||||||
 | 
							has, err := sess.Get(dashboard)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !has {
 | 
				
			||||||
 | 
								return ErrPublicDashboardNotFound
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dashboard, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Retrieves public dashboard configuration
 | 
					// Retrieves public dashboard configuration
 | 
				
			||||||
func (d *PublicDashboardStoreImpl) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) {
 | 
					func (d *PublicDashboardStoreImpl) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) {
 | 
				
			||||||
	if accessToken == "" {
 | 
						if accessToken == "" {
 | 
				
			||||||
| 
						 | 
					@ -58,17 +74,7 @@ func (d *PublicDashboardStoreImpl) GetPublicDashboard(ctx context.Context, acces
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// find dashboard
 | 
						// find dashboard
 | 
				
			||||||
	dashRes := &models.Dashboard{OrgId: pdRes.OrgId, Uid: pdRes.DashboardUid}
 | 
						dashRes, err := d.GetDashboard(ctx, pdRes.DashboardUid)
 | 
				
			||||||
	err = d.sqlStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
 | 
					 | 
				
			||||||
		has, err := sess.Get(dashRes)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !has {
 | 
					 | 
				
			||||||
			return ErrPublicDashboardNotFound
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,29 @@ var DefaultTimeSettings, _ = simplejson.NewJson([]byte(`{}`))
 | 
				
			||||||
// Default time to pass in with seconds rounded
 | 
					// Default time to pass in with seconds rounded
 | 
				
			||||||
var DefaultTime = time.Now().UTC().Round(time.Second)
 | 
					var DefaultTime = time.Now().UTC().Round(time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIntegrationGetDashboard(t *testing.T) {
 | 
				
			||||||
 | 
						var sqlStore *sqlstore.SQLStore
 | 
				
			||||||
 | 
						var dashboardStore *dashboardsDB.DashboardStore
 | 
				
			||||||
 | 
						var publicdashboardStore *PublicDashboardStoreImpl
 | 
				
			||||||
 | 
						var savedDashboard *models.Dashboard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setup := func() {
 | 
				
			||||||
 | 
							sqlStore = sqlstore.InitTestDB(t)
 | 
				
			||||||
 | 
							dashboardStore = dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
 | 
							publicdashboardStore = ProvideStore(sqlStore)
 | 
				
			||||||
 | 
							savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("GetDashboard can get original dashboard by uid", func(t *testing.T) {
 | 
				
			||||||
 | 
							setup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dashboard, err := publicdashboardStore.GetDashboard(context.Background(), savedDashboard.Uid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							require.NoError(t, err)
 | 
				
			||||||
 | 
							require.Equal(t, savedDashboard.Uid, dashboard.Uid)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPublicDashboard
 | 
					// GetPublicDashboard
 | 
				
			||||||
func TestIntegrationGetPublicDashboard(t *testing.T) {
 | 
					func TestIntegrationGetPublicDashboard(t *testing.T) {
 | 
				
			||||||
	var sqlStore *sqlstore.SQLStore
 | 
						var sqlStore *sqlstore.SQLStore
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,10 @@ var (
 | 
				
			||||||
		Reason:     "No Uid for public dashboard specified",
 | 
							Reason:     "No Uid for public dashboard specified",
 | 
				
			||||||
		StatusCode: 400,
 | 
							StatusCode: 400,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						ErrPublicDashboardHasTemplateVariables = PublicDashboardErr{
 | 
				
			||||||
 | 
							Reason:     "Public dashboard has template variables",
 | 
				
			||||||
 | 
							StatusCode: 422,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PublicDashboard struct {
 | 
					type PublicDashboard struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,6 +64,29 @@ func (_m *FakePublicDashboardService) BuildPublicDashboardMetricRequest(ctx cont
 | 
				
			||||||
	return r0, r1
 | 
						return r0, r1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetDashboard provides a mock function with given fields: ctx, dashboardUid
 | 
				
			||||||
 | 
					func (_m *FakePublicDashboardService) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
 | 
				
			||||||
 | 
						ret := _m.Called(ctx, dashboardUid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var r0 *models.Dashboard
 | 
				
			||||||
 | 
						if rf, ok := ret.Get(0).(func(context.Context, string) *models.Dashboard); ok {
 | 
				
			||||||
 | 
							r0 = rf(ctx, dashboardUid)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if ret.Get(0) != nil {
 | 
				
			||||||
 | 
								r0 = ret.Get(0).(*models.Dashboard)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var r1 error
 | 
				
			||||||
 | 
						if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 | 
				
			||||||
 | 
							r1 = rf(ctx, dashboardUid)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							r1 = ret.Error(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r0, r1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPublicDashboard provides a mock function with given fields: ctx, accessToken
 | 
					// GetPublicDashboard provides a mock function with given fields: ctx, accessToken
 | 
				
			||||||
func (_m *FakePublicDashboardService) GetPublicDashboard(ctx context.Context, accessToken string) (*models.Dashboard, error) {
 | 
					func (_m *FakePublicDashboardService) GetPublicDashboard(ctx context.Context, accessToken string) (*models.Dashboard, error) {
 | 
				
			||||||
	ret := _m.Called(ctx, accessToken)
 | 
						ret := _m.Called(ctx, accessToken)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,10 +5,10 @@ package publicdashboards
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	context "context"
 | 
						context "context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	models "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
						models "github.com/grafana/grafana/pkg/models"
 | 
				
			||||||
	mock "github.com/stretchr/testify/mock"
 | 
						mock "github.com/stretchr/testify/mock"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pkgmodels "github.com/grafana/grafana/pkg/models"
 | 
						publicdashboardsmodels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testing "testing"
 | 
						testing "testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -39,25 +39,48 @@ func (_m *FakePublicDashboardStore) GenerateNewPublicDashboardUid(ctx context.Co
 | 
				
			||||||
	return r0, r1
 | 
						return r0, r1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetPublicDashboard provides a mock function with given fields: ctx, accessToken
 | 
					// GetDashboard provides a mock function with given fields: ctx, dashboardUid
 | 
				
			||||||
func (_m *FakePublicDashboardStore) GetPublicDashboard(ctx context.Context, accessToken string) (*models.PublicDashboard, *pkgmodels.Dashboard, error) {
 | 
					func (_m *FakePublicDashboardStore) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
 | 
				
			||||||
	ret := _m.Called(ctx, accessToken)
 | 
						ret := _m.Called(ctx, dashboardUid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r0 *models.PublicDashboard
 | 
						var r0 *models.Dashboard
 | 
				
			||||||
	if rf, ok := ret.Get(0).(func(context.Context, string) *models.PublicDashboard); ok {
 | 
						if rf, ok := ret.Get(0).(func(context.Context, string) *models.Dashboard); ok {
 | 
				
			||||||
		r0 = rf(ctx, accessToken)
 | 
							r0 = rf(ctx, dashboardUid)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ret.Get(0) != nil {
 | 
							if ret.Get(0) != nil {
 | 
				
			||||||
			r0 = ret.Get(0).(*models.PublicDashboard)
 | 
								r0 = ret.Get(0).(*models.Dashboard)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r1 *pkgmodels.Dashboard
 | 
						var r1 error
 | 
				
			||||||
	if rf, ok := ret.Get(1).(func(context.Context, string) *pkgmodels.Dashboard); ok {
 | 
						if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 | 
				
			||||||
 | 
							r1 = rf(ctx, dashboardUid)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							r1 = ret.Error(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return r0, r1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetPublicDashboard provides a mock function with given fields: ctx, accessToken
 | 
				
			||||||
 | 
					func (_m *FakePublicDashboardStore) GetPublicDashboard(ctx context.Context, accessToken string) (*publicdashboardsmodels.PublicDashboard, *models.Dashboard, error) {
 | 
				
			||||||
 | 
						ret := _m.Called(ctx, accessToken)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var r0 *publicdashboardsmodels.PublicDashboard
 | 
				
			||||||
 | 
						if rf, ok := ret.Get(0).(func(context.Context, string) *publicdashboardsmodels.PublicDashboard); ok {
 | 
				
			||||||
 | 
							r0 = rf(ctx, accessToken)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if ret.Get(0) != nil {
 | 
				
			||||||
 | 
								r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var r1 *models.Dashboard
 | 
				
			||||||
 | 
						if rf, ok := ret.Get(1).(func(context.Context, string) *models.Dashboard); ok {
 | 
				
			||||||
		r1 = rf(ctx, accessToken)
 | 
							r1 = rf(ctx, accessToken)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ret.Get(1) != nil {
 | 
							if ret.Get(1) != nil {
 | 
				
			||||||
			r1 = ret.Get(1).(*pkgmodels.Dashboard)
 | 
								r1 = ret.Get(1).(*models.Dashboard)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,15 +95,15 @@ func (_m *FakePublicDashboardStore) GetPublicDashboard(ctx context.Context, acce
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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) (*models.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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r0 *models.PublicDashboard
 | 
						var r0 *publicdashboardsmodels.PublicDashboard
 | 
				
			||||||
	if rf, ok := ret.Get(0).(func(context.Context, int64, string) *models.PublicDashboard); ok {
 | 
						if rf, ok := ret.Get(0).(func(context.Context, int64, string) *publicdashboardsmodels.PublicDashboard); ok {
 | 
				
			||||||
		r0 = rf(ctx, orgId, dashboardUid)
 | 
							r0 = rf(ctx, orgId, dashboardUid)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ret.Get(0) != nil {
 | 
							if ret.Get(0) != nil {
 | 
				
			||||||
			r0 = ret.Get(0).(*models.PublicDashboard)
 | 
								r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,20 +139,20 @@ 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 models.SavePublicDashboardConfigCommand) (*models.PublicDashboard, error) {
 | 
					func (_m *FakePublicDashboardStore) SavePublicDashboardConfig(ctx context.Context, cmd publicdashboardsmodels.SavePublicDashboardConfigCommand) (*publicdashboardsmodels.PublicDashboard, error) {
 | 
				
			||||||
	ret := _m.Called(ctx, cmd)
 | 
						ret := _m.Called(ctx, cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r0 *models.PublicDashboard
 | 
						var r0 *publicdashboardsmodels.PublicDashboard
 | 
				
			||||||
	if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardConfigCommand) *models.PublicDashboard); ok {
 | 
						if rf, ok := ret.Get(0).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) *publicdashboardsmodels.PublicDashboard); ok {
 | 
				
			||||||
		r0 = rf(ctx, cmd)
 | 
							r0 = rf(ctx, cmd)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ret.Get(0) != nil {
 | 
							if ret.Get(0) != nil {
 | 
				
			||||||
			r0 = ret.Get(0).(*models.PublicDashboard)
 | 
								r0 = ret.Get(0).(*publicdashboardsmodels.PublicDashboard)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r1 error
 | 
						var r1 error
 | 
				
			||||||
	if rf, ok := ret.Get(1).(func(context.Context, models.SavePublicDashboardConfigCommand) error); ok {
 | 
						if rf, ok := ret.Get(1).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) error); ok {
 | 
				
			||||||
		r1 = rf(ctx, cmd)
 | 
							r1 = rf(ctx, cmd)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		r1 = ret.Error(1)
 | 
							r1 = ret.Error(1)
 | 
				
			||||||
| 
						 | 
					@ -139,11 +162,11 @@ func (_m *FakePublicDashboardStore) SavePublicDashboardConfig(ctx context.Contex
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdatePublicDashboardConfig provides a mock function with given fields: ctx, cmd
 | 
					// UpdatePublicDashboardConfig provides a mock function with given fields: ctx, cmd
 | 
				
			||||||
func (_m *FakePublicDashboardStore) UpdatePublicDashboardConfig(ctx context.Context, cmd models.SavePublicDashboardConfigCommand) error {
 | 
					func (_m *FakePublicDashboardStore) UpdatePublicDashboardConfig(ctx context.Context, cmd publicdashboardsmodels.SavePublicDashboardConfigCommand) error {
 | 
				
			||||||
	ret := _m.Called(ctx, cmd)
 | 
						ret := _m.Called(ctx, cmd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var r0 error
 | 
						var r0 error
 | 
				
			||||||
	if rf, ok := ret.Get(0).(func(context.Context, models.SavePublicDashboardConfigCommand) error); ok {
 | 
						if rf, ok := ret.Get(0).(func(context.Context, publicdashboardsmodels.SavePublicDashboardConfigCommand) error); ok {
 | 
				
			||||||
		r0 = rf(ctx, cmd)
 | 
							r0 = rf(ctx, cmd)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		r0 = ret.Error(0)
 | 
							r0 = ret.Error(0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import (
 | 
				
			||||||
type Service interface {
 | 
					type Service interface {
 | 
				
			||||||
	BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*models.SignedInUser, error)
 | 
						BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*models.SignedInUser, error)
 | 
				
			||||||
	GetPublicDashboard(ctx context.Context, accessToken string) (*models.Dashboard, error)
 | 
						GetPublicDashboard(ctx context.Context, accessToken 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, 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)
 | 
				
			||||||
| 
						 | 
					@ -23,6 +24,7 @@ type Service interface {
 | 
				
			||||||
//go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go
 | 
					//go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go
 | 
				
			||||||
type Store interface {
 | 
					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)
 | 
				
			||||||
	GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
 | 
						GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid 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) (*PublicDashboard, error)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,10 +10,10 @@ import (
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/components/simplejson"
 | 
						"github.com/grafana/grafana/pkg/components/simplejson"
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/infra/log"
 | 
						"github.com/grafana/grafana/pkg/infra/log"
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/models"
 | 
						"github.com/grafana/grafana/pkg/models"
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/services/dashboards"
 | 
					 | 
				
			||||||
	"github.com/grafana/grafana/pkg/services/datasources"
 | 
						"github.com/grafana/grafana/pkg/services/datasources"
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/services/publicdashboards"
 | 
						"github.com/grafana/grafana/pkg/services/publicdashboards"
 | 
				
			||||||
	. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
						. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
				
			||||||
 | 
						"github.com/grafana/grafana/pkg/services/publicdashboards/validation"
 | 
				
			||||||
	"github.com/grafana/grafana/pkg/setting"
 | 
						"github.com/grafana/grafana/pkg/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,6 +42,16 @@ func ProvideService(
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pd *PublicDashboardServiceImpl) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
 | 
				
			||||||
 | 
						dashboard, err := pd.store.GetDashboard(ctx, dashboardUid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return dashboard, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Gets public dashboard via access token
 | 
					// Gets public dashboard via access token
 | 
				
			||||||
func (pd *PublicDashboardServiceImpl) GetPublicDashboard(ctx context.Context, accessToken string) (*models.Dashboard, error) {
 | 
					func (pd *PublicDashboardServiceImpl) GetPublicDashboard(ctx context.Context, accessToken string) (*models.Dashboard, error) {
 | 
				
			||||||
	pubdash, d, err := pd.store.GetPublicDashboard(ctx, accessToken)
 | 
						pubdash, d, err := pd.store.GetPublicDashboard(ctx, accessToken)
 | 
				
			||||||
| 
						 | 
					@ -78,8 +88,13 @@ 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, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) {
 | 
				
			||||||
	if len(dto.DashboardUid) == 0 {
 | 
						dashboard, err := pd.GetDashboard(ctx, dto.DashboardUid)
 | 
				
			||||||
		return nil, dashboards.ErrDashboardIdentifierNotSet
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = validation.ValidateSavePublicDashboard(dto, dashboard)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// set default value for time settings
 | 
						// set default value for time settings
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ func TestSavePublicDashboard(t *testing.T) {
 | 
				
			||||||
		sqlStore := sqlstore.InitTestDB(t)
 | 
							sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
		dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
							dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
		publicdashboardStore := database.ProvideStore(sqlStore)
 | 
							publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
		dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
							dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		service := &PublicDashboardServiceImpl{
 | 
							service := &PublicDashboardServiceImpl{
 | 
				
			||||||
			log:   log.New("test.logger"),
 | 
								log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -164,7 +164,7 @@ func TestSavePublicDashboard(t *testing.T) {
 | 
				
			||||||
		sqlStore := sqlstore.InitTestDB(t)
 | 
							sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
		dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
							dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
		publicdashboardStore := database.ProvideStore(sqlStore)
 | 
							publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
		dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
							dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		service := &PublicDashboardServiceImpl{
 | 
							service := &PublicDashboardServiceImpl{
 | 
				
			||||||
			log:   log.New("test.logger"),
 | 
								log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -190,7 +190,32 @@ func TestSavePublicDashboard(t *testing.T) {
 | 
				
			||||||
		assert.Equal(t, defaultPubdashTimeSettings, pubdash.TimeSettings)
 | 
							assert.Equal(t, defaultPubdashTimeSettings, pubdash.TimeSettings)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Run("PLACEHOLDER - dashboard with template variables cannot be saved", func(t *testing.T) {})
 | 
						t.Run("Validate pubdash whose dashboard has template variables returns error", func(t *testing.T) {
 | 
				
			||||||
 | 
							sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
 | 
							dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
 | 
							publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
 | 
							templateVars := make([]map[string]interface{}, 1)
 | 
				
			||||||
 | 
							dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							service := &PublicDashboardServiceImpl{
 | 
				
			||||||
 | 
								log:   log.New("test.logger"),
 | 
				
			||||||
 | 
								store: publicdashboardStore,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dto := &SavePublicDashboardConfigDTO{
 | 
				
			||||||
 | 
								DashboardUid: dashboard.Uid,
 | 
				
			||||||
 | 
								OrgId:        dashboard.OrgId,
 | 
				
			||||||
 | 
								UserId:       7,
 | 
				
			||||||
 | 
								PublicDashboard: &PublicDashboard{
 | 
				
			||||||
 | 
									IsEnabled:    true,
 | 
				
			||||||
 | 
									DashboardUid: "NOTTHESAME",
 | 
				
			||||||
 | 
									OrgId:        9999999,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, err := service.SavePublicDashboardConfig(context.Background(), dto)
 | 
				
			||||||
 | 
							require.Error(t, err)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestUpdatePublicDashboard(t *testing.T) {
 | 
					func TestUpdatePublicDashboard(t *testing.T) {
 | 
				
			||||||
| 
						 | 
					@ -198,7 +223,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
 | 
				
			||||||
		sqlStore := sqlstore.InitTestDB(t)
 | 
							sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
		dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
							dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
		publicdashboardStore := database.ProvideStore(sqlStore)
 | 
							publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
		dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
							dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		service := &PublicDashboardServiceImpl{
 | 
							service := &PublicDashboardServiceImpl{
 | 
				
			||||||
			log:   log.New("test.logger"),
 | 
								log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -265,7 +290,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
 | 
				
			||||||
		sqlStore := sqlstore.InitTestDB(t)
 | 
							sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
		dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
							dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
		publicdashboardStore := database.ProvideStore(sqlStore)
 | 
							publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
		dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
							dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		service := &PublicDashboardServiceImpl{
 | 
							service := &PublicDashboardServiceImpl{
 | 
				
			||||||
			log:   log.New("test.logger"),
 | 
								log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -323,7 +348,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
 | 
				
			||||||
func TestBuildAnonymousUser(t *testing.T) {
 | 
					func TestBuildAnonymousUser(t *testing.T) {
 | 
				
			||||||
	sqlStore := sqlstore.InitTestDB(t)
 | 
						sqlStore := sqlstore.InitTestDB(t)
 | 
				
			||||||
	dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
						dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
	dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
						dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
	publicdashboardStore := database.ProvideStore(sqlStore)
 | 
						publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
	service := &PublicDashboardServiceImpl{
 | 
						service := &PublicDashboardServiceImpl{
 | 
				
			||||||
		log:   log.New("test.logger"),
 | 
							log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -346,8 +371,8 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
 | 
				
			||||||
	dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
						dashboardStore := dashboardsDB.ProvideDashboardStore(sqlStore)
 | 
				
			||||||
	publicdashboardStore := database.ProvideStore(sqlStore)
 | 
						publicdashboardStore := database.ProvideStore(sqlStore)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
 | 
						publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
	nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, true)
 | 
						nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, true, []map[string]interface{}{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	service := &PublicDashboardServiceImpl{
 | 
						service := &PublicDashboardServiceImpl{
 | 
				
			||||||
		log:   log.New("test.logger"),
 | 
							log:   log.New("test.logger"),
 | 
				
			||||||
| 
						 | 
					@ -441,7 +466,7 @@ func TestBuildPublicDashboardMetricRequest(t *testing.T) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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, templateVars []map[string]interface{}, tags ...interface{}) *models.Dashboard {
 | 
				
			||||||
	t.Helper()
 | 
						t.Helper()
 | 
				
			||||||
	cmd := models.SaveDashboardCommand{
 | 
						cmd := models.SaveDashboardCommand{
 | 
				
			||||||
		OrgId:    orgId,
 | 
							OrgId:    orgId,
 | 
				
			||||||
| 
						 | 
					@ -490,6 +515,9 @@ func insertTestDashboard(t *testing.T, dashboardStore *dashboardsDB.DashboardSto
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								"templating": map[string]interface{}{
 | 
				
			||||||
 | 
									"list": templateVars,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
		}),
 | 
							}),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	dash, err := dashboardStore.SaveDashboard(cmd)
 | 
						dash, err := dashboardStore.SaveDashboard(cmd)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					package validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/grafana/grafana/pkg/components/simplejson"
 | 
				
			||||||
 | 
						"github.com/grafana/grafana/pkg/models"
 | 
				
			||||||
 | 
						publicdashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) {
 | 
				
			||||||
 | 
							templateVars := []byte(`{
 | 
				
			||||||
 | 
								"templating": {
 | 
				
			||||||
 | 
									 "list": [
 | 
				
			||||||
 | 
									   {
 | 
				
			||||||
 | 
										  "name": "templateVariableName"
 | 
				
			||||||
 | 
									   }
 | 
				
			||||||
 | 
									]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}`)
 | 
				
			||||||
 | 
							dashboardData, _ := simplejson.NewJson(templateVars)
 | 
				
			||||||
 | 
							dashboard := models.NewDashboardFromJson(dashboardData)
 | 
				
			||||||
 | 
							dto := &publicdashboardModels.SavePublicDashboardConfigDTO{DashboardUid: "abc123", OrgId: 1, UserId: 1, PublicDashboard: nil}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := ValidateSavePublicDashboard(dto, dashboard)
 | 
				
			||||||
 | 
							require.ErrorContains(t, err, "Public dashboard has template variables")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.Run("Returns no validation error when dashboard has no template variables", func(t *testing.T) {
 | 
				
			||||||
 | 
							templateVars := []byte(`{
 | 
				
			||||||
 | 
								"templating": {
 | 
				
			||||||
 | 
									 "list": []
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}`)
 | 
				
			||||||
 | 
							dashboardData, _ := simplejson.NewJson(templateVars)
 | 
				
			||||||
 | 
							dashboard := models.NewDashboardFromJson(dashboardData)
 | 
				
			||||||
 | 
							dto := &publicdashboardModels.SavePublicDashboardConfigDTO{DashboardUid: "abc123", OrgId: 1, UserId: 1, PublicDashboard: nil}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err := ValidateSavePublicDashboard(dto, dashboard)
 | 
				
			||||||
 | 
							require.NoError(t, err)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					package validation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/grafana/grafana/pkg/models"
 | 
				
			||||||
 | 
						"github.com/grafana/grafana/pkg/services/dashboards"
 | 
				
			||||||
 | 
						publicDashboardModels "github.com/grafana/grafana/pkg/services/publicdashboards/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ValidateSavePublicDashboard(dto *publicDashboardModels.SavePublicDashboardConfigDTO, dashboard *models.Dashboard) error {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(dto.DashboardUid) == 0 {
 | 
				
			||||||
 | 
							return dashboards.ErrDashboardIdentifierNotSet
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if hasTemplateVariables(dashboard) {
 | 
				
			||||||
 | 
							return publicDashboardModels.ErrPublicDashboardHasTemplateVariables
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hasTemplateVariables(dashboard *models.Dashboard) bool {
 | 
				
			||||||
 | 
						templateVariables := dashboard.Data.Get("templating").Get("list").MustArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return len(templateVariables) > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue