mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Public Dashboards: Query Caching (#51403)
* passes id and uid to PublicDashboardDatasource * betterer results * If for a public dashboard, return the PublicDashboardDataSource first or else getDatasourceSrv.get() will fail bc of no authed user. Added some unit tests for resolving the uid from the many possible datasource types. * updates betterer * Exports DashboardService. Adds method to DashboardService to build anonymous user for use with public dashboards where there is no authed user. Adds method on dashboard_queries to get all dashboard uids from a dashboard. * refactors to get unique datasource uids * Adds tests for getting all unique datasource uids off a dashboard * adds test for building anonymous user with read and query actions that are scoped to each datasource uid in the dashboard * updates casing of DashboardService * updates test case to have additional panel with a different datasource * gives default interval to public dashboard data source
This commit is contained in:
parent
9941e06e22
commit
0b4af38bfa
@ -131,6 +131,37 @@ exports[`better eslint`] = {
|
|||||||
"e2e/dashboards-suite/dashboard-templating.spec.ts:5381": [
|
"e2e/dashboards-suite/dashboard-templating.spec.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
],
|
],
|
||||||
|
"packages/grafana-data/src/types/datasource.ts:1730680024": [
|
||||||
|
[24, 85, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[27, 73, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[46, 28, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[51, 26, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[56, 47, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[99, 46, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[109, 48, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[151, 14, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[152, 25, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[153, 24, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[172, 72, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[246, 37, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[260, 41, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[260, 57, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[270, 26, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[270, 41, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[275, 24, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[280, 25, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[317, 21, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[319, 32, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[382, 14, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[408, 14, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[414, 58, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[608, 13, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[618, 37, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[618, 42, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[619, 43, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[619, 59, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[625, 46, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[626, 22, 3, "Unexpected any. Specify a different type.", "193409811"]
|
||||||
"e2e/dashboards-suite/textbox-variables.spec.ts:5381": [
|
"e2e/dashboards-suite/textbox-variables.spec.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
@ -693,6 +724,11 @@ exports[`better eslint`] = {
|
|||||||
"packages/grafana-data/src/types/flot.ts:5381": [
|
"packages/grafana-data/src/types/flot.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
],
|
],
|
||||||
|
"packages/grafana-runtime/src/utils/PublicDashboardDataSource.test.ts:2092121707": [
|
||||||
|
[14, 19, 123, "Do not use any type assertions.", "3028355264"],
|
||||||
|
[14, 19, 109, "Do not use any type assertions.", "4248357345"],
|
||||||
|
[39, 13, 206, "Do not use any type assertions.", "1200376833"],
|
||||||
|
[72, 49, 54, "Do not use any type assertions.", "114713672"]
|
||||||
"packages/grafana-data/src/types/graph.ts:5381": [
|
"packages/grafana-data/src/types/graph.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
@ -4333,6 +4369,13 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||||
],
|
],
|
||||||
|
"public/app/features/dashboard/services/PublicDashboardDataSource.ts:102072381": [
|
||||||
|
[14, 61, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[20, 12, 16, "Do not use any type assertions.", "1747412709"],
|
||||||
|
[43, 34, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[61, 16, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[78, 45, 22, "Do not use any type assertions.", "1838499175"],
|
||||||
|
[86, 28, 3, "Unexpected any. Specify a different type.", "193409811"]
|
||||||
"public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx:5381": [
|
"public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
@ -5639,6 +5682,17 @@ exports[`better eslint`] = {
|
|||||||
"public/app/features/query/state/DashboardQueryRunner/DashboardQueryRunner.ts:5381": [
|
"public/app/features/query/state/DashboardQueryRunner/DashboardQueryRunner.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
],
|
],
|
||||||
|
"public/app/features/query/state/PanelQueryRunner.ts:3311920590": [
|
||||||
|
[110, 39, 118, "Do not use any type assertions.", "2873233307"],
|
||||||
|
[189, 46, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[238, 5, 14, "Do not use any type assertions.", "4095749936"],
|
||||||
|
[238, 16, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[285, 20, 46, "Do not use any type assertions.", "1712789723"],
|
||||||
|
[285, 20, 32, "Do not use any type assertions.", "2220885232"],
|
||||||
|
[367, 21, 17, "Do not use any type assertions.", "1733699692"],
|
||||||
|
[367, 35, 3, "Unexpected any. Specify a different type.", "193409811"],
|
||||||
|
[368, 11, 27, "Do not use any type assertions.", "2133479311"],
|
||||||
|
[371, 38, 20, "Do not use any type assertions.", "340150831"]
|
||||||
"public/app/features/query/state/DashboardQueryRunner/LegacyAnnotationQueryRunner.test.ts:5381": [
|
"public/app/features/query/state/DashboardQueryRunner/LegacyAnnotationQueryRunner.test.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
@ -483,7 +483,6 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
|||||||
timeInfo?: string; // The query time description (blue text in the upper right)
|
timeInfo?: string; // The query time description (blue text in the upper right)
|
||||||
panelId?: number;
|
panelId?: number;
|
||||||
dashboardId?: number;
|
dashboardId?: number;
|
||||||
// Temporary prop for public dashboards, to be replaced by publicAccessKey
|
|
||||||
publicDashboardAccessToken?: string;
|
publicDashboardAccessToken?: string;
|
||||||
|
|
||||||
// Request Timing
|
// Request Timing
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { BackendSrv, BackendSrvRequest } from 'src/services';
|
import { BackendSrv, BackendSrvRequest } from 'src/services';
|
||||||
|
|
||||||
import { DataQueryRequest, DataSourceRef } from '@grafana/data';
|
import { DataQueryRequest, DataSourceInstanceSettings, DataSourceRef } from '@grafana/data';
|
||||||
|
|
||||||
import { PublicDashboardDataSource } from '../../../../public/app/features/dashboard/services/PublicDashboardDataSource';
|
import {
|
||||||
|
PUBLIC_DATASOURCE,
|
||||||
|
PublicDashboardDataSource,
|
||||||
|
} from '../../../../public/app/features/dashboard/services/PublicDashboardDataSource';
|
||||||
|
|
||||||
|
import { DataSourceWithBackend } from './DataSourceWithBackend';
|
||||||
|
|
||||||
const mockDatasourceRequest = jest.fn();
|
const mockDatasourceRequest = jest.fn();
|
||||||
|
|
||||||
@ -28,7 +33,7 @@ describe('PublicDashboardDatasource', () => {
|
|||||||
mockDatasourceRequest.mockReset();
|
mockDatasourceRequest.mockReset();
|
||||||
mockDatasourceRequest.mockReturnValue(Promise.resolve({}));
|
mockDatasourceRequest.mockReturnValue(Promise.resolve({}));
|
||||||
|
|
||||||
const ds = new PublicDashboardDataSource();
|
const ds = new PublicDashboardDataSource('public');
|
||||||
const panelId = 1;
|
const panelId = 1;
|
||||||
const publicDashboardAccessToken = 'abc123';
|
const publicDashboardAccessToken = 'abc123';
|
||||||
|
|
||||||
@ -47,4 +52,27 @@ describe('PublicDashboardDatasource', () => {
|
|||||||
`/api/public/dashboards/${publicDashboardAccessToken}/panels/${panelId}/query`
|
`/api/public/dashboards/${publicDashboardAccessToken}/panels/${panelId}/query`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('returns public datasource uid when datasource passed in is null', () => {
|
||||||
|
let ds = new PublicDashboardDataSource(null);
|
||||||
|
expect(ds.uid).toBe(PUBLIC_DATASOURCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns datasource when datasource passed in is a string', () => {
|
||||||
|
let ds = new PublicDashboardDataSource('theDatasourceUid');
|
||||||
|
expect(ds.uid).toBe('theDatasourceUid');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns datasource uid when datasource passed in is a DataSourceRef implementation', () => {
|
||||||
|
const datasource = { type: 'datasource', uid: 'abc123' };
|
||||||
|
let ds = new PublicDashboardDataSource(datasource);
|
||||||
|
expect(ds.uid).toBe('abc123');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns datasource uid when datasource passed in is a DatasourceApi instance', () => {
|
||||||
|
const settings: DataSourceInstanceSettings = { id: 1, uid: 'abc123' } as DataSourceInstanceSettings;
|
||||||
|
const datasource = new DataSourceWithBackend(settings);
|
||||||
|
let ds = new PublicDashboardDataSource(datasource);
|
||||||
|
expect(ds.uid).toBe('abc123');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -53,7 +53,7 @@ func (hs *HTTPServer) GetAnnotations(c *models.ReqContext) response.Response {
|
|||||||
item.DashboardUID = val
|
item.DashboardUID = val
|
||||||
} else {
|
} else {
|
||||||
query := models.GetDashboardQuery{Id: item.DashboardId, OrgId: c.OrgId}
|
query := models.GetDashboardQuery{Id: item.DashboardId, OrgId: c.OrgId}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), &query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), &query)
|
||||||
if err == nil && query.Result != nil {
|
if err == nil && query.Result != nil {
|
||||||
item.DashboardUID = &query.Result.Uid
|
item.DashboardUID = &query.Result.Uid
|
||||||
dashboardCache[item.DashboardId] = &query.Result.Uid
|
dashboardCache[item.DashboardId] = &query.Result.Uid
|
||||||
@ -82,7 +82,7 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
|||||||
// overwrite dashboardId when dashboardUID is not empty
|
// overwrite dashboardId when dashboardUID is not empty
|
||||||
if cmd.DashboardUID != "" {
|
if cmd.DashboardUID != "" {
|
||||||
query := models.GetDashboardQuery{OrgId: c.OrgId, Uid: cmd.DashboardUID}
|
query := models.GetDashboardQuery{OrgId: c.OrgId, Uid: cmd.DashboardUID}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), &query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), &query)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd.DashboardId = query.Result.Id
|
cmd.DashboardId = query.Result.Id
|
||||||
}
|
}
|
||||||
@ -291,7 +291,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
|||||||
|
|
||||||
if cmd.DashboardUID != "" {
|
if cmd.DashboardUID != "" {
|
||||||
query := models.GetDashboardQuery{OrgId: c.OrgId, Uid: cmd.DashboardUID}
|
query := models.GetDashboardQuery{OrgId: c.OrgId, Uid: cmd.DashboardUID}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), &query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), &query)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd.DashboardId = query.Result.Id
|
cmd.DashboardId = query.Result.Id
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
|
|||||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||||
hs := setupSimpleHTTPServer(nil)
|
hs := setupSimpleHTTPServer(nil)
|
||||||
hs.SQLStore = store
|
hs.SQLStore = store
|
||||||
hs.dashboardService = dashSvc
|
hs.DashboardService = dashSvc
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
@ -428,7 +428,7 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
|
|||||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||||
hs := setupSimpleHTTPServer(nil)
|
hs := setupSimpleHTTPServer(nil)
|
||||||
hs.SQLStore = store
|
hs.SQLStore = store
|
||||||
hs.dashboardService = dashSvc
|
hs.DashboardService = dashSvc
|
||||||
|
|
||||||
sc := setupScenarioContext(t, url)
|
sc := setupScenarioContext(t, url)
|
||||||
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
||||||
|
@ -28,7 +28,7 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
|
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
|
||||||
reqEditorRole := middleware.ReqEditorRole
|
reqEditorRole := middleware.ReqEditorRole
|
||||||
reqOrgAdmin := middleware.ReqOrgAdmin
|
reqOrgAdmin := middleware.ReqOrgAdmin
|
||||||
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.dashboardService)
|
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.DashboardService)
|
||||||
reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin)
|
reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin)
|
||||||
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
|
||||||
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
|
||||||
|
@ -396,7 +396,7 @@ func setupHTTPServerWithCfgDb(t *testing.T, useFakeAccessControl, enableAccessCo
|
|||||||
AccessControl: ac,
|
AccessControl: ac,
|
||||||
teamPermissionsService: teamPermissionService,
|
teamPermissionsService: teamPermissionService,
|
||||||
searchUsersService: searchusers.ProvideUsersService(db, filters.ProvideOSSSearchUserFilter()),
|
searchUsersService: searchusers.ProvideUsersService(db, filters.ProvideOSSSearchUserFilter()),
|
||||||
dashboardService: dashboardservice.ProvideDashboardService(
|
DashboardService: dashboardservice.ProvideDashboardService(
|
||||||
cfg, dashboardsStore, nil, features,
|
cfg, dashboardsStore, nil, features,
|
||||||
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), ac,
|
accesscontrolmock.NewMockedPermissionsService(), accesscontrolmock.NewMockedPermissionsService(), ac,
|
||||||
),
|
),
|
||||||
|
@ -144,7 +144,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
|
|||||||
// lookup folder title
|
// lookup folder title
|
||||||
if dash.FolderId > 0 {
|
if dash.FolderId > 0 {
|
||||||
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
|
query := models.GetDashboardQuery{Id: dash.FolderId, OrgId: c.OrgId}
|
||||||
if err := hs.dashboardService.GetDashboard(c.Req.Context(), &query); err != nil {
|
if err := hs.DashboardService.GetDashboard(c.Req.Context(), &query); err != nil {
|
||||||
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
if errors.Is(err, dashboards.ErrFolderNotFound) {
|
||||||
return response.Error(404, "Folder not found", err)
|
return response.Error(404, "Folder not found", err)
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ func (hs *HTTPServer) getDashboardHelper(ctx context.Context, orgID int64, id in
|
|||||||
query = models.GetDashboardQuery{Id: id, OrgId: orgID}
|
query = models.GetDashboardQuery{Id: id, OrgId: orgID}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.dashboardService.GetDashboard(ctx, &query); err != nil {
|
if err := hs.DashboardService.GetDashboard(ctx, &query); err != nil {
|
||||||
return nil, response.Error(404, "Dashboard not found", err)
|
return nil, response.Error(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ func (hs *HTTPServer) deleteDashboard(c *models.ReqContext) response.Response {
|
|||||||
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.Id, "user", c.SignedInUser.UserId, "error", err)
|
hs.log.Error("Failed to disconnect library elements", "dashboard", dash.Id, "user", c.SignedInUser.UserId, "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hs.dashboardService.DeleteDashboard(c.Req.Context(), dash.Id, c.OrgId)
|
err = hs.DashboardService.DeleteDashboard(c.Req.Context(), dash.Id, c.OrgId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var dashboardErr dashboards.DashboardErr
|
var dashboardErr dashboards.DashboardErr
|
||||||
if ok := errors.As(err, &dashboardErr); ok {
|
if ok := errors.As(err, &dashboardErr); ok {
|
||||||
@ -387,7 +387,7 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
|
|||||||
Overwrite: cmd.Overwrite,
|
Overwrite: cmd.Overwrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard, err := hs.dashboardService.SaveDashboard(alerting.WithUAEnabled(ctx, hs.Cfg.UnifiedAlerting.IsEnabled()), dashItem, allowUiUpdate)
|
dashboard, err := hs.DashboardService.SaveDashboard(alerting.WithUAEnabled(ctx, hs.Cfg.UnifiedAlerting.IsEnabled()), dashItem, allowUiUpdate)
|
||||||
|
|
||||||
if dashboard != nil && hs.entityEventsService != nil {
|
if dashboard != nil && hs.entityEventsService != nil {
|
||||||
if err := hs.entityEventsService.SaveEvent(ctx, store.SaveEventCmd{
|
if err := hs.entityEventsService.SaveEvent(ctx, store.SaveEventCmd{
|
||||||
@ -460,7 +460,7 @@ func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) response.Response {
|
|||||||
|
|
||||||
if preference.HomeDashboardID != 0 {
|
if preference.HomeDashboardID != 0 {
|
||||||
slugQuery := models.GetDashboardRefByIdQuery{Id: preference.HomeDashboardID}
|
slugQuery := models.GetDashboardRefByIdQuery{Id: preference.HomeDashboardID}
|
||||||
err := hs.dashboardService.GetDashboardUIDById(c.Req.Context(), &slugQuery)
|
err := hs.DashboardService.GetDashboardUIDById(c.Req.Context(), &slugQuery)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
url := models.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
|
url := models.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug)
|
||||||
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
|
dashRedirect := dtos.DashboardRedirect{RedirectUri: url}
|
||||||
@ -546,7 +546,7 @@ func (hs *HTTPServer) GetDashboardVersions(c *models.ReqContext) response.Respon
|
|||||||
OrgId: c.SignedInUser.OrgId,
|
OrgId: c.SignedInUser.OrgId,
|
||||||
Uid: dashUID,
|
Uid: dashUID,
|
||||||
}
|
}
|
||||||
if err := hs.dashboardService.GetDashboard(c.Req.Context(), &q); err != nil {
|
if err := hs.DashboardService.GetDashboard(c.Req.Context(), &q); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "failed to get dashboard by UID", err)
|
return response.Error(http.StatusBadRequest, "failed to get dashboard by UID", err)
|
||||||
}
|
}
|
||||||
dashID = q.Result.Id
|
dashID = q.Result.Id
|
||||||
@ -605,7 +605,7 @@ func (hs *HTTPServer) GetDashboardVersion(c *models.ReqContext) response.Respons
|
|||||||
OrgId: c.SignedInUser.OrgId,
|
OrgId: c.SignedInUser.OrgId,
|
||||||
Uid: dashUID,
|
Uid: dashUID,
|
||||||
}
|
}
|
||||||
if err := hs.dashboardService.GetDashboard(c.Req.Context(), &q); err != nil {
|
if err := hs.DashboardService.GetDashboard(c.Req.Context(), &q); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "failed to get dashboard by UID", err)
|
return response.Error(http.StatusBadRequest, "failed to get dashboard by UID", err)
|
||||||
}
|
}
|
||||||
dashID = q.Result.Id
|
dashID = q.Result.Id
|
||||||
@ -782,7 +782,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *models.ReqContext) response.Res
|
|||||||
|
|
||||||
func (hs *HTTPServer) GetDashboardTags(c *models.ReqContext) {
|
func (hs *HTTPServer) GetDashboardTags(c *models.ReqContext) {
|
||||||
query := models.GetDashboardTagsQuery{OrgId: c.OrgId}
|
query := models.GetDashboardTagsQuery{OrgId: c.OrgId}
|
||||||
err := hs.dashboardService.GetDashboardTags(c.Req.Context(), &query)
|
err := hs.DashboardService.GetDashboardTags(c.Req.Context(), &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JsonApiErr(500, "Failed to get tags from database", err)
|
c.JsonApiErr(500, "Failed to get tags from database", err)
|
||||||
return
|
return
|
||||||
@ -803,7 +803,7 @@ func (hs *HTTPServer) GetDashboardUIDs(c *models.ReqContext) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
q.Id = id
|
q.Id = id
|
||||||
err = hs.dashboardService.GetDashboardUIDById(c.Req.Context(), q)
|
err = hs.DashboardService.GetDashboardUIDById(c.Req.Context(), q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *models.ReqContext) response.
|
|||||||
return response.Success("Dashboard permissions updated")
|
return response.Success("Dashboard permissions updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.dashboardService.UpdateDashboardACL(c.Req.Context(), dashID, items); err != nil {
|
if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), dashID, items); err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
|
if errors.Is(err, models.ErrDashboardAclInfoMissing) ||
|
||||||
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
|
errors.Is(err, models.ErrDashboardPermissionDashboardEmpty) {
|
||||||
return response.Error(409, err.Error(), err)
|
return response.Error(409, err.Error(), err)
|
||||||
|
@ -39,7 +39,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
|||||||
Cfg: settings,
|
Cfg: settings,
|
||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
Features: features,
|
Features: features,
|
||||||
dashboardService: dashboardservice.ProvideDashboardService(
|
DashboardService: dashboardservice.ProvideDashboardService(
|
||||||
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac,
|
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac,
|
||||||
),
|
),
|
||||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
func (hs *HTTPServer) GetPublicDashboard(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetPublicDashboard(c *models.ReqContext) response.Response {
|
||||||
accessToken := web.Params(c.Req)[":accessToken"]
|
accessToken := web.Params(c.Req)[":accessToken"]
|
||||||
|
|
||||||
dash, err := hs.dashboardService.GetPublicDashboard(c.Req.Context(), accessToken)
|
dash, err := hs.DashboardService.GetPublicDashboard(c.Req.Context(), accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard", err)
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard", err)
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ func (hs *HTTPServer) GetPublicDashboard(c *models.ReqContext) response.Response
|
|||||||
|
|
||||||
// gets public dashboard configuration for dashboard
|
// gets public dashboard configuration for dashboard
|
||||||
func (hs *HTTPServer) GetPublicDashboardConfig(c *models.ReqContext) response.Response {
|
func (hs *HTTPServer) GetPublicDashboardConfig(c *models.ReqContext) response.Response {
|
||||||
pdc, err := hs.dashboardService.GetPublicDashboardConfig(c.Req.Context(), c.OrgId, web.Params(c.Req)[":uid"])
|
pdc, err := hs.DashboardService.GetPublicDashboardConfig(c.Req.Context(), c.OrgId, web.Params(c.Req)[":uid"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard config", err)
|
return handleDashboardErr(http.StatusInternalServerError, "Failed to get public dashboard config", err)
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ func (hs *HTTPServer) SavePublicDashboardConfig(c *models.ReqContext) response.R
|
|||||||
PublicDashboard: pubdash,
|
PublicDashboard: pubdash,
|
||||||
}
|
}
|
||||||
|
|
||||||
pubdash, err := hs.dashboardService.SavePublicDashboardConfig(c.Req.Context(), &dto)
|
pubdash, err := hs.DashboardService.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)
|
||||||
}
|
}
|
||||||
@ -87,17 +87,17 @@ func (hs *HTTPServer) QueryPublicDashboard(c *models.ReqContext) response.Respon
|
|||||||
return response.Error(http.StatusBadRequest, "invalid panel ID", err)
|
return response.Error(http.StatusBadRequest, "invalid panel ID", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboard, err := hs.dashboardService.GetPublicDashboard(c.Req.Context(), web.Params(c.Req)[":accessToken"])
|
dashboard, err := hs.DashboardService.GetPublicDashboard(c.Req.Context(), web.Params(c.Req)[":accessToken"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "could not fetch dashboard", err)
|
return response.Error(http.StatusInternalServerError, "could not fetch dashboard", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicDashboard, err := hs.dashboardService.GetPublicDashboardConfig(c.Req.Context(), dashboard.OrgId, dashboard.Uid)
|
publicDashboard, err := hs.DashboardService.GetPublicDashboardConfig(c.Req.Context(), dashboard.OrgId, dashboard.Uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "could not fetch public dashboard", err)
|
return response.Error(http.StatusInternalServerError, "could not fetch public dashboard", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reqDTO, err := hs.dashboardService.BuildPublicDashboardMetricRequest(
|
reqDTO, err := hs.DashboardService.BuildPublicDashboardMetricRequest(
|
||||||
c.Req.Context(),
|
c.Req.Context(),
|
||||||
dashboard,
|
dashboard,
|
||||||
publicDashboard,
|
publicDashboard,
|
||||||
|
@ -38,7 +38,7 @@ func TestAPIGetPublicDashboard(t *testing.T) {
|
|||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||||
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
||||||
Return(&models.Dashboard{}, nil).Maybe()
|
Return(&models.Dashboard{}, nil).Maybe()
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
|
|
||||||
setInitCtxSignedInViewer(sc.initCtx)
|
setInitCtxSignedInViewer(sc.initCtx)
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
@ -97,7 +97,7 @@ func TestAPIGetPublicDashboard(t *testing.T) {
|
|||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||||
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
dashSvc.On("GetPublicDashboard", mock.Anything, mock.AnythingOfType("string")).
|
||||||
Return(test.publicDashboardResult, test.publicDashboardErr)
|
Return(test.publicDashboardResult, test.publicDashboardErr)
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
|
|
||||||
setInitCtxSignedInViewer(sc.initCtx)
|
setInitCtxSignedInViewer(sc.initCtx)
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
@ -170,7 +170,7 @@ func TestAPIGetPublicDashboardConfig(t *testing.T) {
|
|||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||||
dashSvc.On("GetPublicDashboardConfig", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
|
dashSvc.On("GetPublicDashboardConfig", mock.Anything, mock.AnythingOfType("int64"), mock.AnythingOfType("string")).
|
||||||
Return(test.PublicDashboardResult, test.PublicDashboardError)
|
Return(test.PublicDashboardResult, test.PublicDashboardError)
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
|
|
||||||
setInitCtxSignedInViewer(sc.initCtx)
|
setInitCtxSignedInViewer(sc.initCtx)
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
@ -229,7 +229,7 @@ func TestApiSavePublicDashboardConfig(t *testing.T) {
|
|||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
dashSvc := dashboards.NewFakeDashboardService(t)
|
||||||
dashSvc.On("SavePublicDashboardConfig", mock.Anything, mock.AnythingOfType("*dashboards.SavePublicDashboardConfigDTO")).
|
dashSvc.On("SavePublicDashboardConfig", mock.Anything, mock.AnythingOfType("*dashboards.SavePublicDashboardConfigDTO")).
|
||||||
Return(&models.PublicDashboard{IsEnabled: true}, test.saveDashboardError)
|
Return(&models.PublicDashboard{IsEnabled: true}, test.saveDashboardError)
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
|
|
||||||
setInitCtxSignedInViewer(sc.initCtx)
|
setInitCtxSignedInViewer(sc.initCtx)
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
@ -298,7 +298,7 @@ func TestAPIQueryPublicDashboard(t *testing.T) {
|
|||||||
return SetupAPITestServer(t, func(hs *HTTPServer) {
|
return SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.queryDataService = qds
|
hs.queryDataService = qds
|
||||||
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards, enabled)
|
hs.Features = featuremgmt.WithFeatures(featuremgmt.FlagPublicDashboards, enabled)
|
||||||
hs.dashboardService = fakeDashboardService
|
hs.DashboardService = fakeDashboardService
|
||||||
}), fakeDashboardService
|
}), fakeDashboardService
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,7 +593,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pubdash, err := scenario.hs.dashboardService.SavePublicDashboardConfig(context.Background(), savePubDashboardCmd)
|
pubdash, err := scenario.hs.DashboardService.SavePublicDashboardConfig(context.Background(), savePubDashboardCmd)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
response := callAPI(
|
response := callAPI(
|
||||||
|
@ -139,7 +139,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
AccessControl: accesscontrolmock.New(),
|
AccessControl: accesscontrolmock.New(),
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
||||||
@ -259,7 +259,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &mockLibraryElementService{},
|
||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
AccessControl: accesscontrolmock.New(),
|
AccessControl: accesscontrolmock.New(),
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
||||||
@ -900,7 +900,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
|||||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||||
SQLStore: mockSQLStore,
|
SQLStore: mockSQLStore,
|
||||||
AccessControl: accesscontrolmock.New(),
|
AccessControl: accesscontrolmock.New(),
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
||||||
hs.callGetDashboard(sc)
|
hs.callGetDashboard(sc)
|
||||||
@ -954,7 +954,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||||||
cfg, dashboardStore, nil, features,
|
cfg, dashboardStore, nil, features,
|
||||||
folderPermissions, dashboardPermissions, ac,
|
folderPermissions, dashboardPermissions, ac,
|
||||||
),
|
),
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
}
|
}
|
||||||
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
hs.CoremodelStaticRegistry, hs.CoremodelRegistry = setupDashboardCoremodel(t)
|
||||||
|
|
||||||
@ -986,7 +986,7 @@ func (hs *HTTPServer) callGetDashboardVersions(sc *scenarioContext) {
|
|||||||
|
|
||||||
func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T,
|
func (hs *HTTPServer) callDeleteDashboardByUID(t *testing.T,
|
||||||
sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService) {
|
sc *scenarioContext, mockDashboard *dashboards.FakeDashboardService) {
|
||||||
hs.dashboardService = mockDashboard
|
hs.DashboardService = mockDashboard
|
||||||
sc.handlerFunc = hs.DeleteDashboardByUID
|
sc.handlerFunc = hs.DeleteDashboardByUID
|
||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
@ -1018,7 +1018,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
pluginStore: &fakePluginStore{},
|
pluginStore: &fakePluginStore{},
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &mockLibraryElementService{},
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
folderService: folderService,
|
folderService: folderService,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
}
|
}
|
||||||
@ -1088,7 +1088,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
|||||||
QuotaService: "a.QuotaService{Cfg: cfg},
|
QuotaService: "a.QuotaService{Cfg: cfg},
|
||||||
LibraryPanelService: &mockLibraryPanelService{},
|
LibraryPanelService: &mockLibraryPanelService{},
|
||||||
LibraryElementService: &mockLibraryElementService{},
|
LibraryElementService: &mockLibraryElementService{},
|
||||||
dashboardService: mock,
|
DashboardService: mock,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
Features: featuremgmt.WithFeatures(),
|
Features: featuremgmt.WithFeatures(),
|
||||||
dashboardVersionService: fakeDashboardVersionService,
|
dashboardVersionService: fakeDashboardVersionService,
|
||||||
|
@ -70,6 +70,8 @@ type MetricRequest struct {
|
|||||||
// required: false
|
// required: false
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
|
|
||||||
|
PublicDashboardAccessToken string `json:"publicDashboardAccessToken"`
|
||||||
|
|
||||||
HTTPRequest *http.Request `json:"-"`
|
HTTPRequest *http.Request `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *models.ReqContext) response.Res
|
|||||||
return response.Success("Dashboard permissions updated")
|
return response.Success("Dashboard permissions updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := hs.dashboardService.UpdateDashboardACL(c.Req.Context(), folder.Id, items); err != nil {
|
if err := hs.DashboardService.UpdateDashboardACL(c.Req.Context(), folder.Id, items); err != nil {
|
||||||
if errors.Is(err, models.ErrDashboardAclInfoMissing) {
|
if errors.Is(err, models.ErrDashboardAclInfoMissing) {
|
||||||
err = models.ErrFolderAclInfoMissing
|
err = models.ErrFolderAclInfoMissing
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
|||||||
folderService: folderService,
|
folderService: folderService,
|
||||||
folderPermissionsService: folderPermissions,
|
folderPermissionsService: folderPermissions,
|
||||||
dashboardPermissionsService: dashboardPermissions,
|
dashboardPermissionsService: dashboardPermissions,
|
||||||
dashboardService: service.ProvideDashboardService(
|
DashboardService: service.ProvideDashboardService(
|
||||||
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac,
|
settings, dashboardStore, nil, features, folderPermissions, dashboardPermissions, ac,
|
||||||
),
|
),
|
||||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||||
|
@ -148,7 +148,7 @@ type HTTPServer struct {
|
|||||||
authenticator loginpkg.Authenticator
|
authenticator loginpkg.Authenticator
|
||||||
teamPermissionsService accesscontrol.TeamPermissionsService
|
teamPermissionsService accesscontrol.TeamPermissionsService
|
||||||
NotificationService *notifications.NotificationService
|
NotificationService *notifications.NotificationService
|
||||||
dashboardService dashboards.DashboardService
|
DashboardService dashboards.DashboardService
|
||||||
dashboardProvisioningService dashboards.DashboardProvisioningService
|
dashboardProvisioningService dashboards.DashboardProvisioningService
|
||||||
folderService dashboards.FolderService
|
folderService dashboards.FolderService
|
||||||
DatasourcePermissionsService permissions.DatasourcePermissionsService
|
DatasourcePermissionsService permissions.DatasourcePermissionsService
|
||||||
@ -267,7 +267,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
authInfoService: authInfoService,
|
authInfoService: authInfoService,
|
||||||
authenticator: authenticator,
|
authenticator: authenticator,
|
||||||
NotificationService: notificationService,
|
NotificationService: notificationService,
|
||||||
dashboardService: dashboardService,
|
DashboardService: dashboardService,
|
||||||
dashboardProvisioningService: dashboardProvisioningService,
|
dashboardProvisioningService: dashboardProvisioningService,
|
||||||
folderService: folderService,
|
folderService: folderService,
|
||||||
DatasourcePermissionsService: datasourcePermissionsService,
|
DatasourcePermissionsService: datasourcePermissionsService,
|
||||||
|
@ -407,7 +407,7 @@ func (hs *HTTPServer) buildStarredItemsNavLinks(c *models.ReqContext, prefs *pre
|
|||||||
Id: dashboardId,
|
Id: dashboardId,
|
||||||
OrgId: c.OrgId,
|
OrgId: c.OrgId,
|
||||||
}
|
}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), query)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
starredDashboards = append(starredDashboards, query.Result)
|
starredDashboards = append(starredDashboards, query.Result)
|
||||||
}
|
}
|
||||||
@ -674,7 +674,7 @@ func (hs *HTTPServer) buildAdminNavLinks(c *models.ReqContext) []*dtos.NavLink {
|
|||||||
|
|
||||||
func (hs *HTTPServer) editorInAnyFolder(c *models.ReqContext) bool {
|
func (hs *HTTPServer) editorInAnyFolder(c *models.ReqContext) bool {
|
||||||
hasEditPermissionInFoldersQuery := models.HasEditPermissionInFoldersQuery{SignedInUser: c.SignedInUser}
|
hasEditPermissionInFoldersQuery := models.HasEditPermissionInFoldersQuery{SignedInUser: c.SignedInUser}
|
||||||
if err := hs.dashboardService.HasEditPermissionInFolders(c.Req.Context(), &hasEditPermissionInFoldersQuery); err != nil {
|
if err := hs.DashboardService.HasEditPermissionInFolders(c.Req.Context(), &hasEditPermissionInFoldersQuery); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return hasEditPermissionInFoldersQuery.Result
|
return hasEditPermissionInFoldersQuery.Result
|
||||||
|
@ -16,7 +16,7 @@ func (hs *HTTPServer) populateDashboardsByID(ctx context.Context, dashboardByIDs
|
|||||||
|
|
||||||
if len(dashboardByIDs) > 0 {
|
if len(dashboardByIDs) > 0 {
|
||||||
dashboardQuery := models.GetDashboardsQuery{DashboardIds: dashboardByIDs}
|
dashboardQuery := models.GetDashboardsQuery{DashboardIds: dashboardByIDs}
|
||||||
if err := hs.dashboardService.GetDashboards(ctx, &dashboardQuery); err != nil {
|
if err := hs.DashboardService.GetDashboards(ctx, &dashboardQuery); err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func (hs *HTTPServer) SetHomeDashboard(c *models.ReqContext) response.Response {
|
|||||||
dashboardID := cmd.HomeDashboardID
|
dashboardID := cmd.HomeDashboardID
|
||||||
if cmd.HomeDashboardUID != nil {
|
if cmd.HomeDashboardUID != nil {
|
||||||
query := models.GetDashboardQuery{Uid: *cmd.HomeDashboardUID}
|
query := models.GetDashboardQuery{Uid: *cmd.HomeDashboardUID}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), &query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(404, "Dashboard not found", err)
|
return response.Error(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ func (hs *HTTPServer) getPreferencesFor(ctx context.Context, orgID, userID, team
|
|||||||
// when homedashboardID is 0, that means it is the default home dashboard, no UID would be returned in the response
|
// when homedashboardID is 0, that means it is the default home dashboard, no UID would be returned in the response
|
||||||
if preference.HomeDashboardID != 0 {
|
if preference.HomeDashboardID != 0 {
|
||||||
query := models.GetDashboardQuery{Id: preference.HomeDashboardID, OrgId: orgID}
|
query := models.GetDashboardQuery{Id: preference.HomeDashboardID, OrgId: orgID}
|
||||||
err = hs.dashboardService.GetDashboard(ctx, &query)
|
err = hs.DashboardService.GetDashboard(ctx, &query)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dashboardUID = query.Result.Uid
|
dashboardUID = query.Result.Uid
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func (hs *HTTPServer) updatePreferencesFor(ctx context.Context, orgID, userID, t
|
|||||||
dashboardID := dtoCmd.HomeDashboardID
|
dashboardID := dtoCmd.HomeDashboardID
|
||||||
if dtoCmd.HomeDashboardUID != nil {
|
if dtoCmd.HomeDashboardUID != nil {
|
||||||
query := models.GetDashboardQuery{Uid: *dtoCmd.HomeDashboardUID, OrgId: orgID}
|
query := models.GetDashboardQuery{Uid: *dtoCmd.HomeDashboardUID, OrgId: orgID}
|
||||||
err := hs.dashboardService.GetDashboard(ctx, &query)
|
err := hs.DashboardService.GetDashboard(ctx, &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(404, "Dashboard not found", err)
|
return response.Error(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ func (hs *HTTPServer) patchPreferencesFor(ctx context.Context, orgID, userID, te
|
|||||||
dashboardID := dtoCmd.HomeDashboardID
|
dashboardID := dtoCmd.HomeDashboardID
|
||||||
if dtoCmd.HomeDashboardUID != nil {
|
if dtoCmd.HomeDashboardUID != nil {
|
||||||
query := models.GetDashboardQuery{Uid: *dtoCmd.HomeDashboardUID, OrgId: orgID}
|
query := models.GetDashboardQuery{Uid: *dtoCmd.HomeDashboardUID, OrgId: orgID}
|
||||||
err := hs.dashboardService.GetDashboard(ctx, &query)
|
err := hs.DashboardService.GetDashboard(ctx, &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(404, "Dashboard not found", err)
|
return response.Error(404, "Dashboard not found", err)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func TestAPIEndpoint_GetCurrentOrgPreferences_LegacyAccessControl(t *testing.T)
|
|||||||
q.Result = &models.Dashboard{Uid: "home", Id: 1}
|
q.Result = &models.Dashboard{Uid: "home", Id: 1}
|
||||||
}).Return(nil)
|
}).Return(nil)
|
||||||
|
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
|
|
||||||
prefService := preftest.NewPreferenceServiceFake()
|
prefService := preftest.NewPreferenceServiceFake()
|
||||||
prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"}
|
prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"}
|
||||||
@ -169,7 +169,7 @@ func TestAPIEndpoint_PatchUserPreferences(t *testing.T) {
|
|||||||
q := args.Get(1).(*models.GetDashboardQuery)
|
q := args.Get(1).(*models.GetDashboardQuery)
|
||||||
q.Result = &models.Dashboard{Uid: "home", Id: 1}
|
q.Result = &models.Dashboard{Uid: "home", Id: 1}
|
||||||
}).Return(nil)
|
}).Return(nil)
|
||||||
sc.hs.dashboardService = dashSvc
|
sc.hs.DashboardService = dashSvc
|
||||||
t.Run("Returns 200 on success", func(t *testing.T) {
|
t.Run("Returns 200 on success", func(t *testing.T) {
|
||||||
response := callAPI(sc.server, http.MethodPatch, patchUserPreferencesUrl, input, t)
|
response := callAPI(sc.server, http.MethodPatch, patchUserPreferencesUrl, input, t)
|
||||||
assert.Equal(t, http.StatusOK, response.Code)
|
assert.Equal(t, http.StatusOK, response.Code)
|
||||||
|
@ -26,7 +26,7 @@ func (hs *HTTPServer) GetStars(c *models.ReqContext) response.Response {
|
|||||||
Id: dashboardId,
|
Id: dashboardId,
|
||||||
OrgId: c.OrgId,
|
OrgId: c.OrgId,
|
||||||
}
|
}
|
||||||
err := hs.dashboardService.GetDashboard(c.Req.Context(), query)
|
err := hs.DashboardService.GetDashboard(c.Req.Context(), query)
|
||||||
|
|
||||||
// Grafana admin users may have starred dashboards in multiple orgs. This will avoid returning errors when the dashboard is in another org
|
// Grafana admin users may have starred dashboards in multiple orgs. This will avoid returning errors when the dashboard is in another org
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -4,7 +4,23 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetQueriesFromDashboard(dashboard *simplejson.Json) map[int64][]*simplejson.Json {
|
func GetUniqueDashboardDatasourceUids(dashboard *simplejson.Json) []string {
|
||||||
|
var datasourceUids []string
|
||||||
|
exists := map[string]bool{}
|
||||||
|
|
||||||
|
for _, panelObj := range dashboard.Get("panels").MustArray() {
|
||||||
|
panel := simplejson.NewFromAny(panelObj)
|
||||||
|
uid := panel.Get("datasource").Get("uid").MustString()
|
||||||
|
if _, ok := exists[uid]; !ok {
|
||||||
|
datasourceUids = append(datasourceUids, uid)
|
||||||
|
exists[uid] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasourceUids
|
||||||
|
}
|
||||||
|
|
||||||
|
func GroupQueriesByPanelId(dashboard *simplejson.Json) map[int64][]*simplejson.Json {
|
||||||
result := make(map[int64][]*simplejson.Json)
|
result := make(map[int64][]*simplejson.Json)
|
||||||
|
|
||||||
for _, panelObj := range dashboard.Get("panels").MustArray() {
|
for _, panelObj := range dashboard.Get("panels").MustArray() {
|
||||||
|
@ -56,6 +56,79 @@ const (
|
|||||||
"schemaVersion": 35
|
"schemaVersion": 35
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
dashboardWithDuplicateDatasources = `
|
||||||
|
{
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "abc123"
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "abc123"
|
||||||
|
},
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "go_goroutines{job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Panel Title",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "_yxMP8Ynk"
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "_yxMP8Ynk"
|
||||||
|
},
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "go_goroutines{job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Panel Title",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "_yxMP8Ynk"
|
||||||
|
},
|
||||||
|
"id": 3,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "prometheus",
|
||||||
|
"uid": "_yxMP8Ynk"
|
||||||
|
},
|
||||||
|
"exemplar": true,
|
||||||
|
"expr": "go_goroutines{job=\"$job\"}",
|
||||||
|
"interval": "",
|
||||||
|
"legendFormat": "",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Panel Title",
|
||||||
|
"type": "timeseries"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemaVersion": 35
|
||||||
|
}`
|
||||||
|
|
||||||
oldStyleDashboard = `
|
oldStyleDashboard = `
|
||||||
{
|
{
|
||||||
"panels": [
|
"panels": [
|
||||||
@ -79,12 +152,32 @@ const (
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetQueriesFromDashboard(t *testing.T) {
|
func TestGetUniqueDashboardDatasourceUids(t *testing.T) {
|
||||||
|
t.Run("can get unique datasource ids from dashboard", func(t *testing.T) {
|
||||||
|
json, err := simplejson.NewJson([]byte(dashboardWithDuplicateDatasources))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
uids := GetUniqueDashboardDatasourceUids(json)
|
||||||
|
require.Len(t, uids, 2)
|
||||||
|
require.Equal(t, "abc123", uids[0])
|
||||||
|
require.Equal(t, "_yxMP8Ynk", uids[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("can get no datasource uids from empty dashboard", func(t *testing.T) {
|
||||||
|
json, err := simplejson.NewJson([]byte(`{"panels": {}}`))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
uids := GetUniqueDashboardDatasourceUids(json)
|
||||||
|
require.Len(t, uids, 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGroupQueriesByPanelId(t *testing.T) {
|
||||||
t.Run("can extract no queries from empty dashboard", func(t *testing.T) {
|
t.Run("can extract no queries from empty dashboard", func(t *testing.T) {
|
||||||
json, err := simplejson.NewJson([]byte(`{"panels": {}}`))
|
json, err := simplejson.NewJson([]byte(`{"panels": {}}`))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queries := GetQueriesFromDashboard(json)
|
queries := GroupQueriesByPanelId(json)
|
||||||
require.Len(t, queries, 0)
|
require.Len(t, queries, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -92,7 +185,7 @@ func TestGetQueriesFromDashboard(t *testing.T) {
|
|||||||
json, err := simplejson.NewJson([]byte(dashboardWithNoQueries))
|
json, err := simplejson.NewJson([]byte(dashboardWithNoQueries))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queries := GetQueriesFromDashboard(json)
|
queries := GroupQueriesByPanelId(json)
|
||||||
require.Len(t, queries, 1)
|
require.Len(t, queries, 1)
|
||||||
require.Contains(t, queries, int64(2))
|
require.Contains(t, queries, int64(2))
|
||||||
require.Len(t, queries[2], 0)
|
require.Len(t, queries[2], 0)
|
||||||
@ -102,7 +195,7 @@ func TestGetQueriesFromDashboard(t *testing.T) {
|
|||||||
json, err := simplejson.NewJson([]byte(dashboardWithQueries))
|
json, err := simplejson.NewJson([]byte(dashboardWithQueries))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queries := GetQueriesFromDashboard(json)
|
queries := GroupQueriesByPanelId(json)
|
||||||
require.Len(t, queries, 1)
|
require.Len(t, queries, 1)
|
||||||
require.Contains(t, queries, int64(2))
|
require.Contains(t, queries, int64(2))
|
||||||
require.Len(t, queries[2], 2)
|
require.Len(t, queries[2], 2)
|
||||||
@ -138,7 +231,7 @@ func TestGetQueriesFromDashboard(t *testing.T) {
|
|||||||
json, err := simplejson.NewJson([]byte(oldStyleDashboard))
|
json, err := simplejson.NewJson([]byte(oldStyleDashboard))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queries := GetQueriesFromDashboard(json)
|
queries := GroupQueriesByPanelId(json)
|
||||||
require.Len(t, queries, 1)
|
require.Len(t, queries, 1)
|
||||||
require.Contains(t, queries, int64(2))
|
require.Contains(t, queries, int64(2))
|
||||||
require.Len(t, queries[2], 1)
|
require.Len(t, queries[2], 1)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
// DashboardService is a service for operating on dashboards.
|
// DashboardService is a service for operating on dashboards.
|
||||||
type DashboardService interface {
|
type DashboardService interface {
|
||||||
BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error)
|
BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error)
|
||||||
|
BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*models.SignedInUser, error)
|
||||||
BuildSaveDashboardCommand(ctx context.Context, dto *SaveDashboardDTO, shouldValidateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error)
|
BuildSaveDashboardCommand(ctx context.Context, dto *SaveDashboardDTO, shouldValidateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error)
|
||||||
DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error
|
DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error
|
||||||
FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
FindDashboards(ctx context.Context, query *models.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
||||||
|
@ -18,6 +18,29 @@ type FakeDashboardService struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildAnonymousUser provides a mock function with given fields: ctx, dashboard
|
||||||
|
func (_m *FakeDashboardService) BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*models.SignedInUser, error) {
|
||||||
|
ret := _m.Called(ctx, dashboard)
|
||||||
|
|
||||||
|
var r0 *models.SignedInUser
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *models.Dashboard) *models.SignedInUser); ok {
|
||||||
|
r0 = rf(ctx, dashboard)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*models.SignedInUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, *models.Dashboard) error); ok {
|
||||||
|
r1 = rf(ctx, dashboard)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// BuildPublicDashboardMetricRequest provides a mock function with given fields: ctx, dashboard, publicDashboard, panelId
|
// BuildPublicDashboardMetricRequest provides a mock function with given fields: ctx, dashboard, publicDashboard, panelId
|
||||||
func (_m *FakeDashboardService) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error) {
|
func (_m *FakeDashboardService) BuildPublicDashboardMetricRequest(ctx context.Context, dashboard *models.Dashboard, publicDashboard *models.PublicDashboard, panelId int64) (dtos.MetricRequest, error) {
|
||||||
ret := _m.Called(ctx, dashboard, publicDashboard, panelId)
|
ret := _m.Called(ctx, dashboard, publicDashboard, panelId)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"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/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Gets public dashboard via access token
|
// Gets public dashboard via access token
|
||||||
@ -124,7 +125,7 @@ func (dr *DashboardServiceImpl) BuildPublicDashboardMetricRequest(ctx context.Co
|
|||||||
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardNotFound
|
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
queriesByPanel := models.GetQueriesFromDashboard(dashboard.Data)
|
queriesByPanel := models.GroupQueriesByPanelId(dashboard.Data)
|
||||||
|
|
||||||
if _, ok := queriesByPanel[panelId]; !ok {
|
if _, ok := queriesByPanel[panelId]; !ok {
|
||||||
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardPanelNotFound
|
return dtos.MetricRequest{}, dashboards.ErrPublicDashboardPanelNotFound
|
||||||
@ -139,6 +140,26 @@ func (dr *DashboardServiceImpl) BuildPublicDashboardMetricRequest(ctx context.Co
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BuildAnonymousUser creates a user with permissions to read from all datasources used in the dashboard
|
||||||
|
func (dr *DashboardServiceImpl) BuildAnonymousUser(ctx context.Context, dashboard *models.Dashboard) (*models.SignedInUser, error) {
|
||||||
|
datasourceUids := models.GetUniqueDashboardDatasourceUids(dashboard.Data)
|
||||||
|
|
||||||
|
// Create a temp user with read-only datasource permissions
|
||||||
|
anonymousUser := &models.SignedInUser{OrgId: dashboard.OrgId, Permissions: make(map[int64]map[string][]string)}
|
||||||
|
permissions := make(map[string][]string)
|
||||||
|
queryScopes := make([]string, 0)
|
||||||
|
readScopes := make([]string, 0)
|
||||||
|
for _, uid := range datasourceUids {
|
||||||
|
queryScopes = append(queryScopes, fmt.Sprintf("datasources:uid:%s", uid))
|
||||||
|
readScopes = append(readScopes, fmt.Sprintf("datasources:uid:%s", uid))
|
||||||
|
}
|
||||||
|
permissions[datasources.ActionQuery] = queryScopes
|
||||||
|
permissions[datasources.ActionRead] = readScopes
|
||||||
|
anonymousUser.Permissions[dashboard.OrgId] = permissions
|
||||||
|
|
||||||
|
return anonymousUser, nil
|
||||||
|
}
|
||||||
|
|
||||||
// generates a uuid formatted without dashes to use as access token
|
// generates a uuid formatted without dashes to use as access token
|
||||||
func GenerateAccessToken() (string, error) {
|
func GenerateAccessToken() (string, error) {
|
||||||
token, err := uuid.NewRandom()
|
token, err := uuid.NewRandom()
|
||||||
|
@ -314,6 +314,26 @@ func TestUpdatePublicDashboard(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildAnonymousUser(t *testing.T) {
|
||||||
|
sqlStore := sqlstore.InitTestDB(t)
|
||||||
|
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||||
|
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
|
||||||
|
service := &DashboardServiceImpl{
|
||||||
|
log: log.New("test.logger"),
|
||||||
|
dashboardStore: dashboardStore,
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("will add datasource read and query permissions to user for each datasource in dashboard", func(t *testing.T) {
|
||||||
|
user, err := service.BuildAnonymousUser(context.Background(), dashboard)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, dashboard.OrgId, user.OrgId)
|
||||||
|
require.Equal(t, "datasources:uid:ds1", user.Permissions[user.OrgId]["datasources:query"][0])
|
||||||
|
require.Equal(t, "datasources:uid:ds3", user.Permissions[user.OrgId]["datasources:query"][1])
|
||||||
|
require.Equal(t, "datasources:uid:ds1", user.Permissions[user.OrgId]["datasources:read"][0])
|
||||||
|
require.Equal(t, "datasources:uid:ds3", user.Permissions[user.OrgId]["datasources:read"][1])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildPublicDashboardMetricRequest(t *testing.T) {
|
func TestBuildPublicDashboardMetricRequest(t *testing.T) {
|
||||||
sqlStore := sqlstore.InitTestDB(t)
|
sqlStore := sqlstore.InitTestDB(t)
|
||||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||||
@ -425,6 +445,9 @@ func insertTestDashboard(t *testing.T, dashboardStore *database.DashboardStore,
|
|||||||
"panels": []interface{}{
|
"panels": []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"datasource": map[string]interface{}{
|
||||||
|
"uid": "ds1",
|
||||||
|
},
|
||||||
"targets": []interface{}{
|
"targets": []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"datasource": map[string]interface{}{
|
"datasource": map[string]interface{}{
|
||||||
@ -444,6 +467,9 @@ func insertTestDashboard(t *testing.T, dashboardStore *database.DashboardStore,
|
|||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
|
"datasource": map[string]interface{}{
|
||||||
|
"uid": "ds3",
|
||||||
|
},
|
||||||
"targets": []interface{}{
|
"targets": []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"datasource": map[string]interface{}{
|
"datasource": map[string]interface{}{
|
||||||
|
@ -1,19 +1,41 @@
|
|||||||
import { catchError, Observable, of, switchMap } from 'rxjs';
|
import { catchError, Observable, of, switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { DataQuery, DataQueryRequest, DataQueryResponse, DataSourceApi, PluginMeta } from '@grafana/data';
|
import {
|
||||||
|
DataQuery,
|
||||||
|
DataQueryRequest,
|
||||||
|
DataQueryResponse,
|
||||||
|
DataSourceApi,
|
||||||
|
DataSourceRef,
|
||||||
|
PluginMeta,
|
||||||
|
} from '@grafana/data';
|
||||||
import { BackendDataSourceResponse, getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
|
import { BackendDataSourceResponse, getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
|
||||||
|
|
||||||
|
export const PUBLIC_DATASOURCE = '-- Public --';
|
||||||
|
|
||||||
export class PublicDashboardDataSource extends DataSourceApi<any> {
|
export class PublicDashboardDataSource extends DataSourceApi<any> {
|
||||||
constructor() {
|
constructor(datasource: DataSourceRef | string | DataSourceApi | null) {
|
||||||
super({
|
super({
|
||||||
name: 'public-ds',
|
name: 'public-ds',
|
||||||
id: 1,
|
id: 0,
|
||||||
type: 'public-ds',
|
type: 'public-ds',
|
||||||
meta: {} as PluginMeta,
|
meta: {} as PluginMeta,
|
||||||
uid: '1',
|
uid: PublicDashboardDataSource.resolveUid(datasource),
|
||||||
jsonData: {},
|
jsonData: {},
|
||||||
access: 'proxy',
|
access: 'proxy',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.interval = '1min';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the datasource uid based on the many types a datasource can be.
|
||||||
|
*/
|
||||||
|
private static resolveUid(datasource: DataSourceRef | string | DataSourceApi | null): string {
|
||||||
|
if (typeof datasource === 'string') {
|
||||||
|
return datasource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasource?.uid ?? PUBLIC_DATASOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,7 +362,7 @@ async function getDataSource(
|
|||||||
publicDashboardAccessToken?: string
|
publicDashboardAccessToken?: string
|
||||||
): Promise<DataSourceApi> {
|
): Promise<DataSourceApi> {
|
||||||
if (publicDashboardAccessToken) {
|
if (publicDashboardAccessToken) {
|
||||||
return new PublicDashboardDataSource();
|
return new PublicDashboardDataSource(datasource);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (datasource && (datasource as any).query) {
|
if (datasource && (datasource as any).query) {
|
||||||
|
Loading…
Reference in New Issue
Block a user