mirror of https://github.com/grafana/grafana.git
PublicDashboards: Add setting to disable the feature (#78894)
* Replace feature toggle with configuration setting * Fix permission alert * Update documentation * Add back feature toggle * revert unwanted commited changes * fix tests * run prettier * Update SharePublicDashboard.test.tsx * fix linter and frontend tests * Update api.go * Apply docs edit from code review Co-authored-by: Isabel <76437239+imatwawana@users.noreply.github.com> * Update index.md * Update docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * add isPublicDashboardsEnabled + test * fix test * update ff description in registry * move isPublicDashboardsEnabled * revert getConfig() update --------- Co-authored-by: Isabel <76437239+imatwawana@users.noreply.github.com> Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
parent
ef60c90dfa
commit
fdaf6e3f2e
|
|
@ -1762,3 +1762,8 @@ hidden_toggles =
|
||||||
|
|
||||||
# Disables updating specific feature toggles in the feature management page
|
# Disables updating specific feature toggles in the feature management page
|
||||||
read_only_toggles =
|
read_only_toggles =
|
||||||
|
|
||||||
|
#################################### Public Dashboards #####################################
|
||||||
|
[public_dashboards]
|
||||||
|
# Set to false to disable public dashboards
|
||||||
|
enabled = true
|
||||||
|
|
|
||||||
|
|
@ -1615,3 +1615,9 @@
|
||||||
;hidden_toggles =
|
;hidden_toggles =
|
||||||
# Disable updating specific feature toggles in the feature management page
|
# Disable updating specific feature toggles in the feature management page
|
||||||
;read_only_toggles =
|
;read_only_toggles =
|
||||||
|
|
||||||
|
#################################### Public Dashboards #####################################
|
||||||
|
[public_dashboards]
|
||||||
|
# Set to false to disable public dashboards
|
||||||
|
;enabled = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ Dashboard insights show the following information:
|
||||||
|
|
||||||
{{% admonition type="note" %}}
|
{{% admonition type="note" %}}
|
||||||
|
|
||||||
If you've enabled the `publicDashboards` feature toggle, you'll also see a Public dashboards tab in your analytics.
|
If public dashboards are [enabled]({{< relref "../../setup-grafana/configure-grafana/#public_dashboards" >}}), you'll also see a **Public dashboards** tab in your analytics.
|
||||||
|
|
||||||
{{% /admonition %}}
|
{{% /admonition %}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2538,3 +2538,11 @@ Move an app plugin (referenced by its id), including all its pages, to a specifi
|
||||||
|
|
||||||
Move an individual app plugin page (referenced by its `path` field) to a specific navigation section.
|
Move an individual app plugin page (referenced by its `path` field) to a specific navigation section.
|
||||||
Format: `<pageUrl> = <sectionId> <sortWeight>`
|
Format: `<pageUrl> = <sectionId> <sortWeight>`
|
||||||
|
|
||||||
|
## [public_dashboards]
|
||||||
|
|
||||||
|
This section configures the [public dashboards]({{< relref "../../dashboards/dashboard-public" >}}) feature.
|
||||||
|
|
||||||
|
### enabled
|
||||||
|
|
||||||
|
Set this to `false` to disable the public dashboards feature. This prevents users from creating new public dashboards and disables existing ones.
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Some features are enabled by default. You can disable these feature by setting t
|
||||||
| Feature toggle name | Description | Enabled by default |
|
| Feature toggle name | Description | Enabled by default |
|
||||||
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
||||||
| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | |
|
| `disableEnvelopeEncryption` | Disable envelope encryption (emergency only) | |
|
||||||
| `publicDashboards` | Enables public access to dashboards | Yes |
|
| `publicDashboards` | [Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version. | Yes |
|
||||||
| `featureHighlights` | Highlight Grafana Enterprise features | |
|
| `featureHighlights` | Highlight Grafana Enterprise features | |
|
||||||
| `exploreContentOutline` | Content outline sidebar | Yes |
|
| `exploreContentOutline` | Content outline sidebar | Yes |
|
||||||
| `newVizTooltips` | New visualizations tooltips UX | |
|
| `newVizTooltips` | New visualizations tooltips UX | |
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,10 @@ docker run -d -p 3000:3000 --name=grafana \
|
||||||
Grafana supports specifying custom configuration settings using [environment variables]({{< relref "../../../setup-grafana/configure-grafana#override-configuration-with-environment-variables" >}}).
|
Grafana supports specifying custom configuration settings using [environment variables]({{< relref "../../../setup-grafana/configure-grafana#override-configuration-with-environment-variables" >}}).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# enabling public dashboard feature
|
# enable debug logs
|
||||||
|
|
||||||
docker run -d -p 3000:3000 --name=grafana \
|
docker run -d -p 3000:3000 --name=grafana \
|
||||||
-e "GF_FEATURE_TOGGLES_ENABLE=publicDashboards" \
|
-e "GF_LOG_LEVEL=debug" \
|
||||||
grafana/grafana-enterprise
|
grafana/grafana-enterprise
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ export interface BootData {
|
||||||
*/
|
*/
|
||||||
export interface GrafanaConfig {
|
export interface GrafanaConfig {
|
||||||
publicDashboardAccessToken?: string;
|
publicDashboardAccessToken?: string;
|
||||||
|
publicDashboardsEnabled: boolean;
|
||||||
snapshotEnabled: boolean;
|
snapshotEnabled: boolean;
|
||||||
datasources: { [str: string]: DataSourceInstanceSettings };
|
datasources: { [str: string]: DataSourceInstanceSettings };
|
||||||
panels: { [key: string]: PanelPluginMeta };
|
panels: { [key: string]: PanelPluginMeta };
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export type AppPluginConfig = {
|
||||||
|
|
||||||
export class GrafanaBootConfig implements GrafanaConfig {
|
export class GrafanaBootConfig implements GrafanaConfig {
|
||||||
publicDashboardAccessToken?: string;
|
publicDashboardAccessToken?: string;
|
||||||
|
publicDashboardsEnabled = true;
|
||||||
snapshotEnabled = true;
|
snapshotEnabled = true;
|
||||||
datasources: { [str: string]: DataSourceInstanceSettings } = {};
|
datasources: { [str: string]: DataSourceInstanceSettings } = {};
|
||||||
panels: { [key: string]: PanelPluginMeta } = {};
|
panels: { [key: string]: PanelPluginMeta } = {};
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||||
r.Get("/d-embed", reqSignedIn, middleware.AddAllowEmbeddingHeader(), hs.Index)
|
r.Get("/d-embed", reqSignedIn, middleware.AddAllowEmbeddingHeader(), hs.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) && hs.Cfg.PublicDashboardsEnabled {
|
||||||
// list public dashboards
|
// list public dashboards
|
||||||
r.Get("/public-dashboards/list", reqSignedIn, hs.Index)
|
r.Get("/public-dashboards/list", reqSignedIn, hs.Index)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,8 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
// If public dashboards is enabled and we have a public dashboard, update meta
|
// If public dashboards is enabled and we have a public dashboard, update meta values
|
||||||
// values
|
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) && hs.Cfg.PublicDashboardsEnabled {
|
||||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
|
||||||
publicDashboard, err := hs.PublicDashboardsApi.PublicDashboardService.FindByDashboardUid(c.Req.Context(), c.SignedInUser.GetOrgID(), dash.UID)
|
publicDashboard, err := hs.PublicDashboardsApi.PublicDashboardService.FindByDashboardUid(c.Req.Context(), c.SignedInUser.GetOrgID(), dash.UID)
|
||||||
if err != nil && !errors.Is(err, publicdashboardModels.ErrPublicDashboardNotFound) {
|
if err != nil && !errors.Is(err, publicdashboardModels.ErrPublicDashboardNotFound) {
|
||||||
return response.Error(http.StatusInternalServerError, "Error while retrieving public dashboards", err)
|
return response.Error(http.StatusInternalServerError, "Error while retrieving public dashboards", err)
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ func TestHTTPServer_DeleteDashboardByUID_AccessControl(t *testing.T) {
|
||||||
pubDashService := publicdashboards.NewFakePublicDashboardService(t)
|
pubDashService := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
pubDashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil).Maybe()
|
pubDashService.On("DeleteByDashboard", mock.Anything, mock.Anything).Return(nil).Maybe()
|
||||||
middleware := publicdashboards.NewFakePublicDashboardMiddleware(t)
|
middleware := publicdashboards.NewFakePublicDashboardMiddleware(t)
|
||||||
hs.PublicDashboardsApi = api.ProvideApi(pubDashService, nil, hs.AccessControl, featuremgmt.WithFeatures(), middleware)
|
hs.PublicDashboardsApi = api.ProvideApi(pubDashService, nil, hs.AccessControl, featuremgmt.WithFeatures(), middleware, hs.Cfg)
|
||||||
|
|
||||||
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService)
|
guardian.InitAccessControlGuardian(hs.Cfg, hs.AccessControl, hs.DashboardService)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,7 @@ type FrontendSettingsDTO struct {
|
||||||
GeomapDisableCustomBaseLayer bool `json:"geomapDisableCustomBaseLayer"`
|
GeomapDisableCustomBaseLayer bool `json:"geomapDisableCustomBaseLayer"`
|
||||||
|
|
||||||
PublicDashboardAccessToken string `json:"publicDashboardAccessToken"`
|
PublicDashboardAccessToken string `json:"publicDashboardAccessToken"`
|
||||||
|
PublicDashboardsEnabled bool `json:"publicDashboardsEnabled"`
|
||||||
|
|
||||||
DateFormats setting.DateFormats `json:"dateFormats,omitempty"`
|
DateFormats setting.DateFormats `json:"dateFormats,omitempty"`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||||
SecureSocksDSProxyEnabled: hs.Cfg.SecureSocksDSProxy.Enabled && hs.Cfg.SecureSocksDSProxy.ShowUI,
|
SecureSocksDSProxyEnabled: hs.Cfg.SecureSocksDSProxy.Enabled && hs.Cfg.SecureSocksDSProxy.ShowUI,
|
||||||
DisableFrontendSandboxForPlugins: hs.Cfg.DisableFrontendSandboxForPlugins,
|
DisableFrontendSandboxForPlugins: hs.Cfg.DisableFrontendSandboxForPlugins,
|
||||||
PublicDashboardAccessToken: c.PublicDashboardAccessToken,
|
PublicDashboardAccessToken: c.PublicDashboardAccessToken,
|
||||||
|
PublicDashboardsEnabled: hs.Cfg.PublicDashboardsEnabled,
|
||||||
SharedWithMeFolderUID: folder.SharedWithMeFolderUID,
|
SharedWithMeFolderUID: folder.SharedWithMeFolderUID,
|
||||||
|
|
||||||
BuildInfo: dtos.FrontendSettingsBuildInfoDTO{
|
BuildInfo: dtos.FrontendSettingsBuildInfoDTO{
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ var (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "publicDashboards",
|
Name: "publicDashboards",
|
||||||
Description: "Enables public access to dashboards",
|
Description: "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.",
|
||||||
Stage: FeatureStageGeneralAvailability,
|
Stage: FeatureStageGeneralAvailability,
|
||||||
Owner: grafanaSharingSquad,
|
Owner: grafanaSharingSquad,
|
||||||
Expression: "true", // enabled by default
|
Expression: "true", // enabled by default
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const (
|
||||||
FlagPanelTitleSearch = "panelTitleSearch"
|
FlagPanelTitleSearch = "panelTitleSearch"
|
||||||
|
|
||||||
// FlagPublicDashboards
|
// FlagPublicDashboards
|
||||||
// Enables public access to dashboards
|
// [Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.
|
||||||
FlagPublicDashboards = "publicDashboards"
|
FlagPublicDashboards = "publicDashboards"
|
||||||
|
|
||||||
// FlagPublicDashboardsEmailSharing
|
// FlagPublicDashboardsEmailSharing
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||||
Icon: "library-panel",
|
Icon: "library-panel",
|
||||||
})
|
})
|
||||||
|
|
||||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPublicDashboards) {
|
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPublicDashboards) && s.cfg.PublicDashboardsEnabled {
|
||||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||||
Text: "Public dashboards",
|
Text: "Public dashboards",
|
||||||
Id: "dashboards/public",
|
Id: "dashboards/public",
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,19 @@ import (
|
||||||
"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/services/publicdashboards/validation"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Api struct {
|
type Api struct {
|
||||||
PublicDashboardService publicdashboards.Service
|
PublicDashboardService publicdashboards.Service
|
||||||
RouteRegister routing.RouteRegister
|
|
||||||
AccessControl accesscontrol.AccessControl
|
|
||||||
Features *featuremgmt.FeatureManager
|
|
||||||
Log log.Logger
|
|
||||||
Middleware publicdashboards.Middleware
|
Middleware publicdashboards.Middleware
|
||||||
|
|
||||||
|
accessControl accesscontrol.AccessControl
|
||||||
|
cfg *setting.Cfg
|
||||||
|
features *featuremgmt.FeatureManager
|
||||||
|
log log.Logger
|
||||||
|
routeRegister routing.RouteRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideApi(
|
func ProvideApi(
|
||||||
|
|
@ -35,21 +38,27 @@ func ProvideApi(
|
||||||
ac accesscontrol.AccessControl,
|
ac accesscontrol.AccessControl,
|
||||||
features *featuremgmt.FeatureManager,
|
features *featuremgmt.FeatureManager,
|
||||||
md publicdashboards.Middleware,
|
md publicdashboards.Middleware,
|
||||||
|
cfg *setting.Cfg,
|
||||||
) *Api {
|
) *Api {
|
||||||
api := &Api{
|
api := &Api{
|
||||||
PublicDashboardService: pd,
|
PublicDashboardService: pd,
|
||||||
RouteRegister: rr,
|
|
||||||
AccessControl: ac,
|
|
||||||
Features: features,
|
|
||||||
Log: log.New("publicdashboards.api"),
|
|
||||||
Middleware: md,
|
Middleware: md,
|
||||||
|
accessControl: ac,
|
||||||
|
cfg: cfg,
|
||||||
|
features: features,
|
||||||
|
log: log.New("publicdashboards.api"),
|
||||||
|
routeRegister: rr,
|
||||||
}
|
}
|
||||||
|
|
||||||
// attach api if PublicDashboards feature flag is enabled
|
// register endpoints if the feature is enabled
|
||||||
if features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
if features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) && cfg.PublicDashboardsEnabled {
|
||||||
api.RegisterAPIEndpoints()
|
api.RegisterAPIEndpoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
||||||
|
api.log.Warn("[Deprecated] The publicDashboards feature toggle will be removed in Grafana v11. To disable the public dashboards feature, use the public_dashboards.enabled setting.")
|
||||||
|
}
|
||||||
|
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,35 +68,35 @@ func (api *Api) RegisterAPIEndpoints() {
|
||||||
// Anonymous access to public dashboard route is configured in pkg/api/api.go
|
// Anonymous access to public dashboard route is configured in pkg/api/api.go
|
||||||
// because it is deeply dependent on the HTTPServer.Index() method and would result in a
|
// because it is deeply dependent on the HTTPServer.Index() method and would result in a
|
||||||
// circular dependency
|
// circular dependency
|
||||||
api.RouteRegister.Group("/api/public/dashboards/:accessToken", func(apiRoute routing.RouteRegister) {
|
api.routeRegister.Group("/api/public/dashboards/:accessToken", func(apiRoute routing.RouteRegister) {
|
||||||
apiRoute.Get("/", routing.Wrap(api.ViewPublicDashboard))
|
apiRoute.Get("/", routing.Wrap(api.ViewPublicDashboard))
|
||||||
apiRoute.Get("/annotations", routing.Wrap(api.GetPublicAnnotations))
|
apiRoute.Get("/annotations", routing.Wrap(api.GetPublicAnnotations))
|
||||||
apiRoute.Post("/panels/:panelId/query", routing.Wrap(api.QueryPublicDashboard))
|
apiRoute.Post("/panels/:panelId/query", routing.Wrap(api.QueryPublicDashboard))
|
||||||
}, api.Middleware.HandleApi)
|
}, api.Middleware.HandleApi)
|
||||||
|
|
||||||
// Auth endpoints
|
// Auth endpoints
|
||||||
auth := accesscontrol.Middleware(api.AccessControl)
|
auth := accesscontrol.Middleware(api.accessControl)
|
||||||
uidScope := dashboards.ScopeDashboardsProvider.GetResourceScopeUID(accesscontrol.Parameter(":dashboardUid"))
|
uidScope := dashboards.ScopeDashboardsProvider.GetResourceScopeUID(accesscontrol.Parameter(":dashboardUid"))
|
||||||
|
|
||||||
// List public dashboards for org
|
// List public dashboards for org
|
||||||
api.RouteRegister.Get("/api/dashboards/public-dashboards", middleware.ReqSignedIn, routing.Wrap(api.ListPublicDashboards))
|
api.routeRegister.Get("/api/dashboards/public-dashboards", middleware.ReqSignedIn, routing.Wrap(api.ListPublicDashboards))
|
||||||
// Get public dashboard
|
// Get public dashboard
|
||||||
api.RouteRegister.Get("/api/dashboards/uid/:dashboardUid/public-dashboards",
|
api.routeRegister.Get("/api/dashboards/uid/:dashboardUid/public-dashboards",
|
||||||
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, uidScope)),
|
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, uidScope)),
|
||||||
routing.Wrap(api.GetPublicDashboard))
|
routing.Wrap(api.GetPublicDashboard))
|
||||||
|
|
||||||
// Create Public Dashboard
|
// Create Public Dashboard
|
||||||
api.RouteRegister.Post("/api/dashboards/uid/:dashboardUid/public-dashboards",
|
api.routeRegister.Post("/api/dashboards/uid/:dashboardUid/public-dashboards",
|
||||||
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
||||||
routing.Wrap(api.CreatePublicDashboard))
|
routing.Wrap(api.CreatePublicDashboard))
|
||||||
|
|
||||||
// Update Public Dashboard
|
// Update Public Dashboard
|
||||||
api.RouteRegister.Patch("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
api.routeRegister.Patch("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
||||||
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
||||||
routing.Wrap(api.UpdatePublicDashboard))
|
routing.Wrap(api.UpdatePublicDashboard))
|
||||||
|
|
||||||
// Delete Public dashboard
|
// Delete Public dashboard
|
||||||
api.RouteRegister.Delete("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
api.routeRegister.Delete("/api/dashboards/uid/:dashboardUid/public-dashboards/:uid",
|
||||||
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
auth(accesscontrol.EvalPermission(dashboards.ActionDashboardsPublicWrite, uidScope)),
|
||||||
routing.Wrap(api.DeletePublicDashboard))
|
routing.Wrap(api.DeletePublicDashboard))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"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"
|
||||||
|
|
@ -27,7 +26,7 @@ var userAdmin = &user.SignedInUser{UserID: 2, OrgID: 1, OrgRole: org.RoleAdmin,
|
||||||
var userViewer = &user.SignedInUser{UserID: 4, OrgID: 1, OrgRole: org.RoleViewer, Login: "testViewerUserRBAC", Permissions: map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll}}}}
|
var userViewer = &user.SignedInUser{UserID: 4, OrgID: 1, OrgRole: org.RoleViewer, Login: "testViewerUserRBAC", Permissions: map[int64]map[string][]string{1: {dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsAll}}}}
|
||||||
var anonymousUser = &user.SignedInUser{IsAnonymous: true}
|
var anonymousUser = &user.SignedInUser{IsAnonymous: true}
|
||||||
|
|
||||||
func TestAPIFeatureFlag(t *testing.T) {
|
func TestAPIFeatureDisabled(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Method string
|
Method string
|
||||||
|
|
@ -71,11 +70,18 @@ func TestAPIFeatureFlag(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name+" - setting disabled", func(t *testing.T) {
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
|
cfg.PublicDashboardsEnabled = false
|
||||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
features := featuremgmt.WithFeatures()
|
testServer := setupTestServer(t, cfg, service, userAdmin, true)
|
||||||
testServer := setupTestServer(t, cfg, features, service, nil, userAdmin)
|
response := callAPI(testServer, test.Method, test.Path, nil, t)
|
||||||
|
assert.Equal(t, http.StatusNotFound, response.Code)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run(test.Name+" - feature flag disabled", func(t *testing.T) {
|
||||||
|
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
|
testServer := setupTestServer(t, nil, service, userAdmin, false)
|
||||||
response := callAPI(testServer, test.Method, test.Path, nil, t)
|
response := callAPI(testServer, test.Method, test.Path, nil, t)
|
||||||
assert.Equal(t, http.StatusNotFound, response.Code)
|
assert.Equal(t, http.StatusNotFound, response.Code)
|
||||||
})
|
})
|
||||||
|
|
@ -130,9 +136,7 @@ func TestAPIListPublicDashboard(t *testing.T) {
|
||||||
service.On("FindAllWithPagination", mock.Anything, mock.Anything, mock.Anything).
|
service.On("FindAllWithPagination", mock.Anything, mock.Anything, mock.Anything).
|
||||||
Return(test.Response, test.ResponseErr).Maybe()
|
Return(test.Response, test.ResponseErr).Maybe()
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, test.User, true)
|
||||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards)
|
|
||||||
testServer := setupTestServer(t, cfg, features, service, nil, test.User)
|
|
||||||
|
|
||||||
response := callAPI(testServer, http.MethodGet, "/api/dashboards/public-dashboards", nil, t)
|
response := callAPI(testServer, http.MethodGet, "/api/dashboards/public-dashboards", nil, t)
|
||||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||||
|
|
@ -259,10 +263,7 @@ func TestAPIDeletePublicDashboard(t *testing.T) {
|
||||||
Return(test.ResponseErr)
|
Return(test.ResponseErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, test.User, true)
|
||||||
|
|
||||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards)
|
|
||||||
testServer := setupTestServer(t, cfg, features, service, nil, test.User)
|
|
||||||
|
|
||||||
response := callAPI(testServer, http.MethodDelete, fmt.Sprintf("/api/dashboards/uid/%s/public-dashboards/%s", test.DashboardUid, test.PublicDashboardUid), nil, t)
|
response := callAPI(testServer, http.MethodDelete, fmt.Sprintf("/api/dashboards/uid/%s/public-dashboards/%s", test.DashboardUid, test.PublicDashboardUid), nil, t)
|
||||||
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
|
||||||
|
|
@ -347,16 +348,7 @@ func TestAPIGetPublicDashboard(t *testing.T) {
|
||||||
Return(test.PublicDashboardResult, test.PublicDashboardErr)
|
Return(test.PublicDashboardResult, test.PublicDashboardErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, test.User, true)
|
||||||
|
|
||||||
testServer := setupTestServer(
|
|
||||||
t,
|
|
||||||
cfg,
|
|
||||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
|
||||||
service,
|
|
||||||
nil,
|
|
||||||
test.User,
|
|
||||||
)
|
|
||||||
|
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
testServer,
|
testServer,
|
||||||
|
|
@ -474,16 +466,7 @@ func TestApiCreatePublicDashboard(t *testing.T) {
|
||||||
Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr)
|
Return(&PublicDashboard{IsEnabled: true}, test.SaveDashboardErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, test.User, true)
|
||||||
|
|
||||||
testServer := setupTestServer(
|
|
||||||
t,
|
|
||||||
cfg,
|
|
||||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
|
||||||
service,
|
|
||||||
nil,
|
|
||||||
test.User,
|
|
||||||
)
|
|
||||||
|
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
testServer,
|
testServer,
|
||||||
|
|
@ -609,9 +592,6 @@ func TestAPIUpdatePublicDashboard(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
cfg := setting.NewCfg()
|
|
||||||
features := featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards)
|
|
||||||
|
|
||||||
t.Run(test.Name, func(t *testing.T) {
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
|
|
||||||
|
|
@ -620,7 +600,7 @@ func TestAPIUpdatePublicDashboard(t *testing.T) {
|
||||||
Return(test.ExpectedResponse, test.ExpectedError)
|
Return(test.ExpectedResponse, test.ExpectedError)
|
||||||
}
|
}
|
||||||
|
|
||||||
testServer := setupTestServer(t, cfg, features, service, nil, test.User)
|
testServer := setupTestServer(t, nil, service, test.User, true)
|
||||||
url := fmt.Sprintf("/api/dashboards/uid/%s/public-dashboards/%s", test.DashboardUid, test.PublicDashboardUid)
|
url := fmt.Sprintf("/api/dashboards/uid/%s/public-dashboards/%s", test.DashboardUid, test.PublicDashboardUid)
|
||||||
body := strings.NewReader(test.Body)
|
body := strings.NewReader(test.Body)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,12 @@ import (
|
||||||
func setupTestServer(
|
func setupTestServer(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
cfg *setting.Cfg,
|
cfg *setting.Cfg,
|
||||||
features *featuremgmt.FeatureManager,
|
|
||||||
service publicdashboards.Service,
|
service publicdashboards.Service,
|
||||||
db db.DB,
|
|
||||||
user *user.SignedInUser,
|
user *user.SignedInUser,
|
||||||
|
ffEnabled bool,
|
||||||
) *web.Mux {
|
) *web.Mux {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
// build router to register routes
|
// build router to register routes
|
||||||
rr := routing.NewRouteRegister()
|
rr := routing.NewRouteRegister()
|
||||||
|
|
||||||
|
|
@ -56,9 +57,18 @@ func setupTestServer(
|
||||||
// set initial context
|
// set initial context
|
||||||
m.Use(contextProvider(&testContext{user}))
|
m.Use(contextProvider(&testContext{user}))
|
||||||
|
|
||||||
// build api, this will mount the routes at the same time if
|
features := featuremgmt.WithFeatures()
|
||||||
// featuremgmt.FlagPublicDashboard is enabled
|
if ffEnabled {
|
||||||
ProvideApi(service, rr, ac, features, &Middleware{})
|
features = featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = setting.NewCfg()
|
||||||
|
cfg.PublicDashboardsEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// build api, this will mount the routes at the same time if the feature is enabled
|
||||||
|
ProvideApi(service, rr, ac, features, &Middleware{}, cfg)
|
||||||
|
|
||||||
// connect routes to mux
|
// connect routes to mux
|
||||||
rr.Register(m.Router)
|
rr.Register(m.Router)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (api *Api) QueryPublicDashboard(c *contextmodel.ReqContext) response.Respon
|
||||||
return response.Err(err)
|
return response.Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return toJsonStreamingResponse(c.Req.Context(), api.Features, resp)
|
return toJsonStreamingResponse(c.Req.Context(), api.features, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route GET /public/dashboards/{accessToken}/annotations dashboard_public getPublicAnnotations
|
// swagger:route GET /public/dashboards/{accessToken}/annotations dashboard_public getPublicAnnotations
|
||||||
|
|
|
||||||
|
|
@ -101,16 +101,7 @@ func TestAPIViewPublicDashboard(t *testing.T) {
|
||||||
service.On("GetPublicDashboardForView", mock.Anything, mock.AnythingOfType("string")).
|
service.On("GetPublicDashboardForView", mock.Anything, mock.AnythingOfType("string")).
|
||||||
Return(test.DashboardResult, test.Err).Maybe()
|
Return(test.DashboardResult, test.Err).Maybe()
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, anonymousUser, true)
|
||||||
|
|
||||||
testServer := setupTestServer(
|
|
||||||
t,
|
|
||||||
cfg,
|
|
||||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
|
||||||
service,
|
|
||||||
nil,
|
|
||||||
anonymousUser,
|
|
||||||
)
|
|
||||||
|
|
||||||
response := callAPI(testServer, http.MethodGet,
|
response := callAPI(testServer, http.MethodGet,
|
||||||
fmt.Sprintf("/api/public/dashboards/%s", test.AccessToken),
|
fmt.Sprintf("/api/public/dashboards/%s", test.AccessToken),
|
||||||
|
|
@ -202,16 +193,7 @@ func TestAPIQueryPublicDashboard(t *testing.T) {
|
||||||
|
|
||||||
setup := func(enabled bool) (*web.Mux, *publicdashboards.FakePublicDashboardService) {
|
setup := func(enabled bool) (*web.Mux, *publicdashboards.FakePublicDashboardService) {
|
||||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
cfg := setting.NewCfg()
|
testServer := setupTestServer(t, nil, service, anonymousUser, true)
|
||||||
|
|
||||||
testServer := setupTestServer(
|
|
||||||
t,
|
|
||||||
cfg,
|
|
||||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards, enabled),
|
|
||||||
service,
|
|
||||||
nil,
|
|
||||||
anonymousUser,
|
|
||||||
)
|
|
||||||
|
|
||||||
return testServer, service
|
return testServer, service
|
||||||
}
|
}
|
||||||
|
|
@ -338,6 +320,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
||||||
// create public dashboard
|
// create public dashboard
|
||||||
store := publicdashboardsStore.ProvideStore(db, db.Cfg, featuremgmt.WithFeatures())
|
store := publicdashboardsStore.ProvideStore(db, db.Cfg, featuremgmt.WithFeatures())
|
||||||
cfg := setting.NewCfg()
|
cfg := setting.NewCfg()
|
||||||
|
cfg.PublicDashboardsEnabled = true
|
||||||
ac := acmock.New()
|
ac := acmock.New()
|
||||||
ws := publicdashboardsService.ProvideServiceWrapper(store)
|
ws := publicdashboardsService.ProvideServiceWrapper(store)
|
||||||
folderStore := folderimpl.ProvideDashboardFolderStore(db)
|
folderStore := folderimpl.ProvideDashboardFolderStore(db)
|
||||||
|
|
@ -354,13 +337,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// setup test server
|
// setup test server
|
||||||
server := setupTestServer(t,
|
server := setupTestServer(t, cfg, pds, anonymousUser, true)
|
||||||
cfg,
|
|
||||||
featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards),
|
|
||||||
pds,
|
|
||||||
db,
|
|
||||||
anonymousUser,
|
|
||||||
)
|
|
||||||
|
|
||||||
resp := callAPI(server, http.MethodPost,
|
resp := callAPI(server, http.MethodPost,
|
||||||
fmt.Sprintf("/api/public/dashboards/%s/panels/1/query", pubdash.AccessToken),
|
fmt.Sprintf("/api/public/dashboards/%s/panels/1/query", pubdash.AccessToken),
|
||||||
|
|
@ -436,7 +413,6 @@ func TestAPIGetAnnotations(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) {
|
||||||
cfg := setting.NewCfg()
|
|
||||||
service := publicdashboards.NewFakePublicDashboardService(t)
|
service := publicdashboards.NewFakePublicDashboardService(t)
|
||||||
|
|
||||||
if test.ExpectedServiceCalled {
|
if test.ExpectedServiceCalled {
|
||||||
|
|
@ -444,7 +420,7 @@ func TestAPIGetAnnotations(t *testing.T) {
|
||||||
Return(test.Annotations, test.ServiceError).Once()
|
Return(test.Annotations, test.ServiceError).Once()
|
||||||
}
|
}
|
||||||
|
|
||||||
testServer := setupTestServer(t, cfg, featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards), service, nil, anonymousUser)
|
testServer := setupTestServer(t, nil, service, anonymousUser, true)
|
||||||
|
|
||||||
path := fmt.Sprintf("/api/public/dashboards/%s/annotations?from=%s&to=%s", test.AccessToken, test.From, test.To)
|
path := fmt.Sprintf("/api/public/dashboards/%s/annotations?from=%s&to=%s", test.AccessToken, test.From, test.To)
|
||||||
response := callAPI(testServer, http.MethodGet, path, nil, t)
|
response := callAPI(testServer, http.MethodGet, path, nil, t)
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ func TestIntegrationListPublicDashboard(t *testing.T) {
|
||||||
var publicdashboardStore *PublicDashboardStoreImpl
|
var publicdashboardStore *PublicDashboardStoreImpl
|
||||||
|
|
||||||
setup := func() {
|
setup := func() {
|
||||||
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
|
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{})
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -448,7 +448,7 @@ func TestIntegrationCreatePublicDashboard(t *testing.T) {
|
||||||
var savedDashboard2 *dashboards.Dashboard
|
var savedDashboard2 *dashboards.Dashboard
|
||||||
|
|
||||||
setup := func() {
|
setup := func() {
|
||||||
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
|
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{})
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -528,7 +528,7 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
setup := func() {
|
setup := func() {
|
||||||
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
|
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{})
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -788,7 +788,7 @@ func TestGetMetrics(t *testing.T) {
|
||||||
var savedDashboard4 *dashboards.Dashboard
|
var savedDashboard4 *dashboards.Dashboard
|
||||||
|
|
||||||
setup := func() {
|
setup := func() {
|
||||||
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
|
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{})
|
||||||
quotaService := quotatest.New(false, nil)
|
quotaService := quotatest.New(false, nil)
|
||||||
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore), quotaService)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,9 @@ type Cfg struct {
|
||||||
// sqlstore package and HTTP middlewares.
|
// sqlstore package and HTTP middlewares.
|
||||||
DatabaseInstrumentQueries bool
|
DatabaseInstrumentQueries bool
|
||||||
|
|
||||||
|
// Public dashboards
|
||||||
|
PublicDashboardsEnabled bool
|
||||||
|
|
||||||
// Feature Management Settings
|
// Feature Management Settings
|
||||||
FeatureManagement FeatureMgmtSettings
|
FeatureManagement FeatureMgmtSettings
|
||||||
}
|
}
|
||||||
|
|
@ -1254,6 +1257,7 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
|
||||||
cfg.UserFacingDefaultError = logSection.Key("user_facing_default_error").MustString("please inspect Grafana server log for details")
|
cfg.UserFacingDefaultError = logSection.Key("user_facing_default_error").MustString("please inspect Grafana server log for details")
|
||||||
|
|
||||||
cfg.readFeatureManagementConfig()
|
cfg.readFeatureManagementConfig()
|
||||||
|
cfg.readPublicDashboardsSettings()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1965,3 +1969,8 @@ func (cfg *Cfg) readLiveSettings(iniFile *ini.File) error {
|
||||||
cfg.LiveAllowedOrigins = originPatterns
|
cfg.LiveAllowedOrigins = originPatterns
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Cfg) readPublicDashboardsSettings() {
|
||||||
|
publicDashboards := cfg.Raw.Section("public_dashboards")
|
||||||
|
cfg.PublicDashboardsEnabled = publicDashboards.Key("enabled").MustBool(true)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||||
import { config, featureEnabled } from '@grafana/runtime';
|
import { config, featureEnabled } from '@grafana/runtime';
|
||||||
import { useStyles2, TabsBar, Tab } from '@grafana/ui';
|
import { useStyles2, TabsBar, Tab } from '@grafana/ui';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
||||||
|
|
||||||
import { Page } from '../../core/components/Page/Page';
|
import { Page } from '../../core/components/Page/Page';
|
||||||
import { AccessControlAction } from '../../types';
|
import { AccessControlAction } from '../../types';
|
||||||
|
|
@ -46,7 +47,7 @@ export default function UserListPage() {
|
||||||
const hasAccessToAdminUsers = contextSrv.hasPermission(AccessControlAction.UsersRead);
|
const hasAccessToAdminUsers = contextSrv.hasPermission(AccessControlAction.UsersRead);
|
||||||
const hasAccessToOrgUsers = contextSrv.hasPermission(AccessControlAction.OrgUsersRead);
|
const hasAccessToOrgUsers = contextSrv.hasPermission(AccessControlAction.OrgUsersRead);
|
||||||
const hasEmailSharingEnabled =
|
const hasEmailSharingEnabled =
|
||||||
Boolean(config.featureToggles.publicDashboards) &&
|
isPublicDashboardsEnabled() &&
|
||||||
Boolean(config.featureToggles.publicDashboardsEmailSharing) &&
|
Boolean(config.featureToggles.publicDashboardsEmailSharing) &&
|
||||||
featureEnabled('publicDashboardsEmailSharing');
|
featureEnabled('publicDashboardsEmailSharing');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { SceneComponentProps, SceneObjectBase, SceneObjectState, VizPanel, Scene
|
||||||
import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
|
import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
|
import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { DashboardInteractions } from '../utils/interactions';
|
import { DashboardInteractions } from '../utils/interactions';
|
||||||
|
|
@ -61,7 +62,7 @@ export class ShareModal extends SceneObjectBase<ShareModalState> implements Moda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Boolean(config.featureToggles['publicDashboards'])) {
|
if (isPublicDashboardsEnabled()) {
|
||||||
tabs.push(new SharePublicDashboardTab({ dashboardRef, modalRef: this.getRef() }));
|
tabs.push(new SharePublicDashboardTab({ dashboardRef, modalRef: this.getRef() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { config } from 'app/core/config';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { SharePublicDashboard } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
import { SharePublicDashboard } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
|
||||||
|
import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||||
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
import { isPanelModelLibraryPanel } from 'app/features/library-panels/guard';
|
||||||
|
|
@ -56,7 +57,7 @@ function getTabs(panel?: PanelModel, activeTab?: string) {
|
||||||
tabs.push(...customDashboardTabs);
|
tabs.push(...customDashboardTabs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Boolean(config.featureToggles['publicDashboards'])) {
|
if (isPublicDashboardsEnabled()) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
label: 'Public dashboard',
|
label: 'Public dashboard',
|
||||||
value: shareDashboardType.publicDashboard,
|
value: shareDashboardType.publicDashboard,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ export const NoUpsertPermissionsAlert = ({ mode }: { mode: 'create' | 'edit' })
|
||||||
data-testid={selectors.NoUpsertPermissionsWarningAlert}
|
data-testid={selectors.NoUpsertPermissionsWarningAlert}
|
||||||
bottomSpacing={0}
|
bottomSpacing={0}
|
||||||
>
|
>
|
||||||
Contact your admin to get permission to {mode} create public dashboards
|
Contact your admin to get permission to {mode} public dashboards
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ beforeAll(() => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config.featureToggles.publicDashboards = true;
|
config.featureToggles.publicDashboards = true;
|
||||||
|
config.publicDashboardsEnabled = true;
|
||||||
|
|
||||||
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
|
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
|
||||||
jest.spyOn(contextSrv, 'hasRole').mockReturnValue(true);
|
jest.spyOn(contextSrv, 'hasRole').mockReturnValue(true);
|
||||||
|
|
@ -137,7 +138,14 @@ describe('SharePublic', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
server.use(getExistentPublicDashboardResponse());
|
server.use(getExistentPublicDashboardResponse());
|
||||||
});
|
});
|
||||||
it('does not render share panel when public dashboards feature is disabled', async () => {
|
it('does not render share panel when public dashboards feature is disabled using config setting', async () => {
|
||||||
|
config.publicDashboardsEnabled = false;
|
||||||
|
await renderSharePublicDashboard(undefined, false);
|
||||||
|
|
||||||
|
expect(screen.getByRole('tablist')).toHaveTextContent('Link');
|
||||||
|
expect(screen.getByRole('tablist')).not.toHaveTextContent('Public dashboard');
|
||||||
|
});
|
||||||
|
it('does not render share panel when public dashboards feature is disabled using feature toggle', async () => {
|
||||||
config.featureToggles.publicDashboards = false;
|
config.featureToggles.publicDashboards = false;
|
||||||
await renderSharePublicDashboard(undefined, false);
|
await renderSharePublicDashboard(undefined, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { TypedVariableModel } from '@grafana/data';
|
import { TypedVariableModel } from '@grafana/data';
|
||||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
import { config, DataSourceWithBackend } from '@grafana/runtime';
|
||||||
import { getConfig } from 'app/core/config';
|
import { getConfig } from 'app/core/config';
|
||||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||||
|
|
||||||
|
|
@ -93,3 +93,7 @@ export const generatePublicDashboardConfigUrl = (dashboardUid: string, dashboard
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validEmailRegex = /^[A-Z\d._%+-]+@[A-Z\d.-]+\.[A-Z]{2,}$/i;
|
export const validEmailRegex = /^[A-Z\d._%+-]+@[A-Z\d.-]+\.[A-Z]{2,}$/i;
|
||||||
|
|
||||||
|
export const isPublicDashboardsEnabled = () => {
|
||||||
|
return Boolean(config.featureToggles.publicDashboards) && config.publicDashboardsEnabled;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,33 @@ import { RouteDescriptor } from '../../core/navigation/types';
|
||||||
import { DashboardRoutes } from '../../types';
|
import { DashboardRoutes } from '../../types';
|
||||||
|
|
||||||
export const getPublicDashboardRoutes = (): RouteDescriptor[] => {
|
export const getPublicDashboardRoutes = (): RouteDescriptor[] => {
|
||||||
if (config.featureToggles.publicDashboards) {
|
if (!config.publicDashboardsEnabled || !config.featureToggles.publicDashboards) {
|
||||||
return [
|
return [];
|
||||||
{
|
|
||||||
path: '/dashboard/public',
|
|
||||||
pageClass: 'page-dashboard',
|
|
||||||
routeName: DashboardRoutes.Public,
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "ListPublicDashboardPage" */ '../../features/manage-dashboards/PublicDashboardListPage'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/public-dashboards/:accessToken',
|
|
||||||
pageClass: 'page-dashboard',
|
|
||||||
routeName: DashboardRoutes.Public,
|
|
||||||
chromeless: true,
|
|
||||||
component: SafeDynamicImport(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "PublicDashboardPage" */ '../../features/dashboard/containers/PublicDashboardPage'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
return [];
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: '/dashboard/public',
|
||||||
|
pageClass: 'page-dashboard',
|
||||||
|
routeName: DashboardRoutes.Public,
|
||||||
|
component: SafeDynamicImport(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "ListPublicDashboardPage" */ '../../features/manage-dashboards/PublicDashboardListPage'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/public-dashboards/:accessToken',
|
||||||
|
pageClass: 'page-dashboard',
|
||||||
|
routeName: DashboardRoutes.Public,
|
||||||
|
chromeless: true,
|
||||||
|
component: SafeDynamicImport(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "PublicDashboardPage" */ '../../features/dashboard/containers/PublicDashboardPage'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue