From 3bc13e23350ab1614453b58cc3b4ec6a39db4b1f Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Tue, 19 Jul 2022 17:44:41 -0600 Subject: [PATCH] Public Dashboards: Add Public Tag to Dashboard Title (#52351) Adds Public tag to dashboard title when it has an enabled public dashboard --- pkg/api/dashboard.go | 13 +++++ pkg/api/dashboard_test.go | 4 ++ pkg/api/dtos/dashboard.go | 1 + .../dashboards/dashboard_service_mock.go | 2 +- pkg/services/dashboards/store_mock.go | 2 +- .../publicdashboards/database/database.go | 18 +++++++ .../database/database_test.go | 50 ++++++++++++++++++- .../public_dashboard_service_mock.go | 23 ++++++++- .../public_dashboard_store_mock.go | 23 ++++++++- .../publicdashboards/publicdashboard.go | 2 + .../publicdashboards/service/service.go | 4 ++ .../dashboard/components/DashNav/DashNav.tsx | 6 ++- public/app/types/dashboard.ts | 1 + 13 files changed, 143 insertions(+), 6 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 3f4c5c78f68..0be4bc7e377 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -77,6 +77,18 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { if rsp != nil { return rsp } + + var ( + hasPublicDashboard bool + err error + ) + if hs.Features.IsEnabled(featuremgmt.FlagPublicDashboards) { + hasPublicDashboard, err = hs.PublicDashboardsApi.PublicDashboardService.PublicDashboardEnabled(c.Req.Context(), dash.Uid) + if err != nil { + return response.Error(500, "Error while retrieving public dashboards", err) + } + } + // When dash contains only keys id, uid that means dashboard data is not valid and json decode failed. if dash.Data != nil { isEmptyData := true @@ -139,6 +151,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response { Url: dash.GetUrl(), FolderTitle: "General", AnnotationsPermissions: annotationPermissions, + PublicDashboardEnabled: hasPublicDashboard, } // lookup folder title diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 9dc4d894e8e..28e04b1e315 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -261,6 +261,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { AccessControl: accesscontrolmock.New(), DashboardService: dashboardService, dashboardVersionService: fakeDashboardVersionService, + Features: featuremgmt.WithFeatures(), } hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t) @@ -901,6 +902,7 @@ func TestDashboardAPIEndpoint(t *testing.T) { SQLStore: mockSQLStore, AccessControl: accesscontrolmock.New(), DashboardService: dashboardService, + Features: featuremgmt.WithFeatures(), } hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t) hs.callGetDashboard(sc) @@ -955,6 +957,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr folderPermissions, dashboardPermissions, ac, ), DashboardService: dashboardService, + Features: featuremgmt.WithFeatures(), } hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t) @@ -1053,6 +1056,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string LibraryElementService: &mockLibraryElementService{}, SQLStore: sqlmock, dashboardVersionService: fakeDashboardVersionService, + Features: featuremgmt.WithFeatures(), } hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t) diff --git a/pkg/api/dtos/dashboard.go b/pkg/api/dtos/dashboard.go index 4b72bcc440e..3d3499c2109 100644 --- a/pkg/api/dtos/dashboard.go +++ b/pkg/api/dtos/dashboard.go @@ -34,6 +34,7 @@ type DashboardMeta struct { ProvisionedExternalId string `json:"provisionedExternalId"` AnnotationsPermissions *AnnotationPermission `json:"annotationsPermissions"` PublicDashboardAccessToken string `json:"publicDashboardAccessToken"` + PublicDashboardEnabled bool `json:"publicDashboardEnabled"` } type AnnotationPermission struct { Dashboard AnnotationActions `json:"dashboard"` diff --git a/pkg/services/dashboards/dashboard_service_mock.go b/pkg/services/dashboards/dashboard_service_mock.go index 0b9e3486468..4a52a07335e 100644 --- a/pkg/services/dashboards/dashboard_service_mock.go +++ b/pkg/services/dashboards/dashboard_service_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.12.2. DO NOT EDIT. +// Code generated by mockery v2.12.1. DO NOT EDIT. package dashboards diff --git a/pkg/services/dashboards/store_mock.go b/pkg/services/dashboards/store_mock.go index 63af437bd35..4e506704aa9 100644 --- a/pkg/services/dashboards/store_mock.go +++ b/pkg/services/dashboards/store_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.12.2. DO NOT EDIT. +// Code generated by mockery v2.12.1. DO NOT EDIT. package dashboards diff --git a/pkg/services/publicdashboards/database/database.go b/pkg/services/publicdashboards/database/database.go index d89687c1526..9375b087a59 100644 --- a/pkg/services/publicdashboards/database/database.go +++ b/pkg/services/publicdashboards/database/database.go @@ -171,3 +171,21 @@ func (d *PublicDashboardStoreImpl) UpdatePublicDashboardConfig(ctx context.Conte return err } + +func (d *PublicDashboardStoreImpl) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { + hasPublicDashboard := false + err := d.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error { + sql := "SELECT COUNT(*) FROM dashboard_public WHERE dashboard_uid=? AND is_enabled=true" + + result, err := dbSession.SQL(sql, dashboardUid).Count() + if err != nil { + return err + } + + hasPublicDashboard = result > 0 + + return err + }) + + return hasPublicDashboard, err +} diff --git a/pkg/services/publicdashboards/database/database_test.go b/pkg/services/publicdashboards/database/database_test.go index ec335f13714..801c2cb461f 100644 --- a/pkg/services/publicdashboards/database/database_test.go +++ b/pkg/services/publicdashboards/database/database_test.go @@ -10,7 +10,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" - dashboards "github.com/grafana/grafana/pkg/services/dashboards" + "github.com/grafana/grafana/pkg/services/dashboards" dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/featuremgmt" . "github.com/grafana/grafana/pkg/services/publicdashboards/models" @@ -38,6 +38,54 @@ func TestIntegrationGetPublicDashboard(t *testing.T) { savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true) } + t.Run("PublicDashboardEnabled Will return true when dashboard has at least one enabled public dashboard", func(t *testing.T) { + setup() + + _, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ + DashboardUid: savedDashboard.Uid, + OrgId: savedDashboard.OrgId, + PublicDashboard: PublicDashboard{ + IsEnabled: true, + Uid: "abc123", + DashboardUid: savedDashboard.Uid, + OrgId: savedDashboard.OrgId, + CreatedAt: time.Now(), + CreatedBy: 7, + AccessToken: "NOTAREALUUID", + }, + }) + require.NoError(t, err) + + res, err := publicdashboardStore.PublicDashboardEnabled(context.Background(), savedDashboard.Uid) + require.NoError(t, err) + + require.True(t, res) + }) + + t.Run("PublicDashboardEnabled will return false when dashboard has public dashboards but they are not enabled", func(t *testing.T) { + setup() + + _, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ + DashboardUid: savedDashboard.Uid, + OrgId: savedDashboard.OrgId, + PublicDashboard: PublicDashboard{ + IsEnabled: false, + Uid: "abc123", + DashboardUid: savedDashboard.Uid, + OrgId: savedDashboard.OrgId, + CreatedAt: time.Now(), + CreatedBy: 7, + AccessToken: "NOTAREALUUID", + }, + }) + require.NoError(t, err) + + res, err := publicdashboardStore.PublicDashboardEnabled(context.Background(), savedDashboard.Uid) + require.NoError(t, err) + + require.False(t, res) + }) + t.Run("returns PublicDashboard and Dashboard", func(t *testing.T) { setup() pubdash, err := publicdashboardStore.SavePublicDashboardConfig(context.Background(), SavePublicDashboardConfigCommand{ diff --git a/pkg/services/publicdashboards/public_dashboard_service_mock.go b/pkg/services/publicdashboards/public_dashboard_service_mock.go index 1eb14f6c5ce..f071f3df777 100644 --- a/pkg/services/publicdashboards/public_dashboard_service_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_service_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.12.2. DO NOT EDIT. +// Code generated by mockery v2.12.1. DO NOT EDIT. package publicdashboards @@ -110,6 +110,27 @@ func (_m *FakePublicDashboardService) GetPublicDashboardConfig(ctx context.Conte return r0, r1 } +// PublicDashboardEnabled provides a mock function with given fields: ctx, dashboardUid +func (_m *FakePublicDashboardService) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { + ret := _m.Called(ctx, dashboardUid) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(ctx, dashboardUid) + } else { + r0 = ret.Get(0).(bool) + } + + 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 +} + // SavePublicDashboardConfig provides a mock function with given fields: ctx, dto func (_m *FakePublicDashboardService) SavePublicDashboardConfig(ctx context.Context, dto *publicdashboardsmodels.SavePublicDashboardConfigDTO) (*publicdashboardsmodels.PublicDashboard, error) { ret := _m.Called(ctx, dto) diff --git a/pkg/services/publicdashboards/public_dashboard_store_mock.go b/pkg/services/publicdashboards/public_dashboard_store_mock.go index 20102fc1179..9ceaf435371 100644 --- a/pkg/services/publicdashboards/public_dashboard_store_mock.go +++ b/pkg/services/publicdashboards/public_dashboard_store_mock.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.12.2. DO NOT EDIT. +// Code generated by mockery v2.12.1. DO NOT EDIT. package publicdashboards @@ -94,6 +94,27 @@ func (_m *FakePublicDashboardStore) GetPublicDashboardConfig(ctx context.Context return r0, r1 } +// PublicDashboardEnabled provides a mock function with given fields: ctx, dashboardUid +func (_m *FakePublicDashboardStore) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { + ret := _m.Called(ctx, dashboardUid) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(ctx, dashboardUid) + } else { + r0 = ret.Get(0).(bool) + } + + 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 +} + // SavePublicDashboardConfig provides a mock function with given fields: ctx, cmd func (_m *FakePublicDashboardStore) SavePublicDashboardConfig(ctx context.Context, cmd models.SavePublicDashboardConfigCommand) (*models.PublicDashboard, error) { ret := _m.Called(ctx, cmd) diff --git a/pkg/services/publicdashboards/publicdashboard.go b/pkg/services/publicdashboards/publicdashboard.go index 4d3be2b3fa7..33fdffd6ec6 100644 --- a/pkg/services/publicdashboards/publicdashboard.go +++ b/pkg/services/publicdashboards/publicdashboard.go @@ -17,6 +17,7 @@ type Service interface { GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) SavePublicDashboardConfig(ctx context.Context, dto *SavePublicDashboardConfigDTO) (*PublicDashboard, error) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *PublicDashboard, panelId int64) (dtos.MetricRequest, error) + PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) } //go:generate mockery --name Store --structname FakePublicDashboardStore --inpackage --filename public_dashboard_store_mock.go @@ -26,4 +27,5 @@ type Store interface { GenerateNewPublicDashboardUid(ctx context.Context) (string, error) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) (*PublicDashboard, error) UpdatePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error + PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) } diff --git a/pkg/services/publicdashboards/service/service.go b/pkg/services/publicdashboards/service/service.go index c1f69db5463..caa7149858e 100644 --- a/pkg/services/publicdashboards/service/service.go +++ b/pkg/services/publicdashboards/service/service.go @@ -189,6 +189,10 @@ func (pd *PublicDashboardServiceImpl) BuildAnonymousUser(ctx context.Context, da return anonymousUser, nil } +func (pd *PublicDashboardServiceImpl) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) { + return pd.store.PublicDashboardEnabled(ctx, dashboardUid) +} + // generates a uuid formatted without dashes to use as access token func GenerateAccessToken() (string, error) { token, err := uuid.NewRandom() diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 653a4f71747..3d096d18dea 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -4,7 +4,7 @@ import { useLocation } from 'react-router-dom'; import { locationUtil, textUtil } from '@grafana/data'; import { locationService } from '@grafana/runtime'; -import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar, useForceUpdate } from '@grafana/ui'; +import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar, useForceUpdate, Tag } from '@grafana/ui'; import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate'; import { NavToolbarSeparator } from 'app/core/components/AppChrome/NavToolbarSeparator'; import config from 'app/core/config'; @@ -154,6 +154,10 @@ export const DashNav = React.memo((props) => { ); } + if (dashboard.meta.publicDashboardEnabled) { + buttons.push(); + } + if (dashboard.uid && config.featureToggles.dashboardComments) { buttons.push( diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index 9d284001174..5739ec3c3ff 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -42,6 +42,7 @@ export interface DashboardMeta { hasUnsavedFolderChange?: boolean; annotationsPermissions?: AnnotationsPermissions; publicDashboardAccessToken?: string; + publicDashboardEnabled?: boolean; } export interface AnnotationActions {