diff --git a/.betterer.results b/.betterer.results index 9a048fec628..de7040910eb 100644 --- a/.betterer.results +++ b/.betterer.results @@ -3016,7 +3016,8 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "4"] ], "public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"] + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] ], "public/app/features/dashboard-scene/utils/test-utils.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], diff --git a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap index 2f12945fd5f..e0bf6e65536 100644 --- a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap +++ b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap @@ -123,6 +123,11 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back ", "mode": "markdown", }, + "targets": [ + { + "refId": "A", + }, + ], "title": "", "transformations": [], "transparent": false, @@ -266,6 +271,10 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to "links": [], "panels": [ { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, "fieldConfig": { "defaults": { "color": { @@ -298,6 +307,18 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to "sort": "none", }, }, + "targets": [ + { + "alias": "series", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + }, + ], "title": "Simple time series graph ", "transformations": [], "transparent": false, @@ -317,6 +338,10 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to "type": "row", }, { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, "fieldConfig": { "defaults": {}, "overrides": [], @@ -329,6 +354,18 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to }, "id": 29, "options": {}, + "targets": [ + { + "alias": "series", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A", + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + }, + ], "title": "panel inside row", "transformations": [], "transparent": false, @@ -355,6 +392,11 @@ exports[`transformSceneToSaveModel Given a simple scene Should transform back to "content": "content", "mode": "markdown", }, + "targets": [ + { + "refId": "A", + }, + ], "title": "Transparent text panel", "transformations": [], "transparent": true, diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts index 3cc466b03c2..03b817b5760 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts @@ -8,6 +8,7 @@ import { } from '@grafana/scenes'; import { Panel, RowPanel } from '@grafana/schema'; import { PanelModel } from 'app/features/dashboard/state'; +import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; @@ -131,6 +132,189 @@ describe('transformSceneToSaveModel', () => { expect(saveModel.annotations?.list?.[3].hide).toEqual(false); }); }); + + describe('Queries', () => { + it('Given panel with queries', () => { + const panel = buildGridItemFromPanelSchema({ + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + maxDataPoints: 100, + targets: [ + { + refId: 'A', + expr: 'A', + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + }, + { + refId: 'B', + expr: 'B', + }, + ], + }); + + const result = gridItemToPanel(panel); + + expect(result.maxDataPoints).toBe(100); + expect(result.targets?.length).toBe(2); + expect(result.targets?.[0]).toEqual({ + refId: 'A', + expr: 'A', + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + }); + + expect(result.datasource).toEqual({ + type: 'grafana-testdata', + uid: 'abc', + }); + }); + + it('Given panel with transformations', () => { + const panel = buildGridItemFromPanelSchema({ + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + maxDataPoints: 100, + + transformations: [ + { + id: 'reduce', + options: { + reducers: ['max'], + mode: 'reduceFields', + includeTimeField: false, + }, + }, + ], + + targets: [ + { + refId: 'A', + expr: 'A', + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + }, + { + refId: 'B', + expr: 'B', + }, + ], + }); + + const result = gridItemToPanel(panel); + + expect(result.transformations.length).toBe(1); + + expect(result.maxDataPoints).toBe(100); + expect(result.targets?.length).toBe(2); + expect(result.targets?.[0]).toEqual({ + refId: 'A', + expr: 'A', + datasource: { + type: 'grafana-testdata', + uid: 'abc', + }, + }); + + expect(result.datasource).toEqual({ + type: 'grafana-testdata', + uid: 'abc', + }); + }); + it('Given panel with shared query', () => { + const panel = buildGridItemFromPanelSchema({ + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + targets: [ + { + refId: 'A', + panelId: 1, + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + }, + ], + }); + + const result = gridItemToPanel(panel); + + expect(result.targets?.length).toBe(1); + expect(result.targets?.[0]).toEqual({ + refId: 'A', + panelId: 1, + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + }); + + expect(result.datasource).toEqual({ + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }); + }); + + it('Given panel with shared query and transformations', () => { + const panel = buildGridItemFromPanelSchema({ + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + targets: [ + { + refId: 'A', + panelId: 1, + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + }, + ], + transformations: [ + { + id: 'reduce', + options: { + reducers: ['max'], + mode: 'reduceFields', + includeTimeField: false, + }, + }, + ], + }); + + const result = gridItemToPanel(panel); + + expect(result.transformations.length).toBe(1); + + expect(result.targets?.length).toBe(1); + expect(result.targets?.[0]).toEqual({ + refId: 'A', + panelId: 1, + datasource: { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }, + }); + + expect(result.datasource).toEqual({ + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }); + }); + }); }); export function buildGridItemFromPanelSchema(panel: Partial): SceneGridItemLike { diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index 91c308eb81c..d572b6134e4 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -7,14 +7,26 @@ import { VizPanel, dataLayers, SceneDataLayerProvider, + SceneQueryRunner, + SceneDataTransformer, } from '@grafana/scenes'; -import { AnnotationQuery, Dashboard, defaultDashboard, FieldConfigSource, Panel, RowPanel } from '@grafana/schema'; +import { + AnnotationQuery, + Dashboard, + DataTransformerConfig, + defaultDashboard, + FieldConfigSource, + Panel, + RowPanel, +} from '@grafana/schema'; import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object'; +import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { DashboardScene } from '../scene/DashboardScene'; import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem'; import { PanelTimeRange } from '../scene/PanelTimeRange'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; +import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; import { getPanelIdForVizPanel } from '../utils/utils'; export function transformSceneToSaveModel(scene: DashboardScene): Dashboard { @@ -118,6 +130,58 @@ export function gridItemToPanel(gridItem: SceneGridItemLike): Panel { panel.hideTimeOverride = panelTime.state.hideTimeOverride; } + const dataProvider = vizPanel.state.$data; + + // Dashboard datasource handling + if (dataProvider instanceof ShareQueryDataProvider) { + panel.datasource = { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }; + panel.targets = [ + { + datasource: { ...panel.datasource }, + refId: 'A', + panelId: dataProvider.state.query.panelId, + topic: dataProvider.state.query.topic, + }, + ]; + } + + // Regular queries handling + if (dataProvider instanceof SceneQueryRunner) { + panel.targets = dataProvider.state.queries; + panel.maxDataPoints = dataProvider.state.maxDataPoints; + panel.datasource = dataProvider.state.datasource; + } + + // Transformations handling + if (dataProvider instanceof SceneDataTransformer) { + const panelData = dataProvider.state.$data; + if (panelData instanceof ShareQueryDataProvider) { + panel.datasource = { + type: 'datasource', + uid: SHARED_DASHBOARD_QUERY, + }; + panel.targets = [ + { + datasource: { ...panel.datasource }, + refId: 'A', + panelId: panelData.state.query.panelId, + topic: panelData.state.query.topic, + }, + ]; + } + + if (panelData instanceof SceneQueryRunner) { + panel.targets = panelData.state.queries; + panel.maxDataPoints = panelData.state.maxDataPoints; + panel.datasource = panelData.state.datasource; + } + + panel.transformations = dataProvider.state.transformations as DataTransformerConfig[]; + } + if (vizPanel.state.displayMode === 'transparent') { panel.transparent = true; } diff --git a/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts b/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts index 0e659cd5fe9..1ee8fc2c44e 100644 --- a/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts +++ b/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts @@ -22,6 +22,7 @@ export function createPanelDataProvider(panel: PanelModel): SceneDataProvider | dataProvider = new ShareQueryDataProvider({ query: panel.targets[0] }); } else { dataProvider = new SceneQueryRunner({ + datasource: panel.datasource ?? undefined, queries: panel.targets, maxDataPoints: panel.maxDataPoints ?? undefined, dataLayerFilter: {