diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx index 24976dcbc0b..891ffe9e780 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataPane.tsx @@ -3,19 +3,15 @@ import { Unsubscribable } from 'rxjs'; import { SceneComponentProps, - SceneDataTransformer, SceneObjectBase, SceneObjectState, SceneObjectUrlSyncConfig, SceneObjectUrlValues, - SceneQueryRunner, VizPanel, - sceneGraph, } from '@grafana/scenes'; import { Tab, TabContent, TabsBar } from '@grafana/ui'; import { shouldShowAlertingTab } from 'app/features/dashboard/components/PanelEditor/state/selectors'; -import { ShareQueryDataProvider } from '../../scene/ShareQueryDataProvider'; import { VizPanelManager } from '../VizPanelManager'; import { PanelDataAlertingTab } from './PanelDataAlertingTab'; @@ -96,30 +92,10 @@ export class PanelDataPane extends SceneObjectBase { }); } - private getDataObjects(): [SceneQueryRunner | ShareQueryDataProvider | undefined, SceneDataTransformer | undefined] { - const dataObj = sceneGraph.getData(this.panelManager.state.panel); - - let runner: SceneQueryRunner | ShareQueryDataProvider | undefined; - let transformer: SceneDataTransformer | undefined; - - if (dataObj instanceof SceneQueryRunner || dataObj instanceof ShareQueryDataProvider) { - runner = dataObj; - } - - if (dataObj instanceof SceneDataTransformer) { - transformer = dataObj; - if (transformer.state.$data instanceof SceneQueryRunner) { - runner = transformer.state.$data; - } - } - - return [runner, transformer]; - } - private buildTabs() { const panelManager = this.panelManager; const panel = panelManager.state.panel; - const [runner] = this.getDataObjects(); + const runner = this.panelManager.queryRunner; const tabs: PanelDataPaneTab[] = []; if (panel) { @@ -129,6 +105,7 @@ export class PanelDataPane extends SceneObjectBase { this.setState({ tabs }); return; } + if (plugin.meta.skipDataQuery) { this.setState({ tabs }); return; diff --git a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx index 50066071d17..54ec7264f33 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelDataPane/PanelDataQueriesTab.tsx @@ -1,16 +1,14 @@ import React from 'react'; import { DataSourceApi, DataSourceInstanceSettings, IconName } from '@grafana/data'; -import { SceneObjectBase, SceneComponentProps, SceneQueryRunner, sceneGraph } from '@grafana/scenes'; +import { SceneObjectBase, SceneComponentProps, sceneGraph } from '@grafana/scenes'; import { DataQuery } from '@grafana/schema'; import { QueryEditorRows } from 'app/features/query/components/QueryEditorRows'; import { QueryGroupTopSection } from 'app/features/query/components/QueryGroup'; -import { DashboardQueryEditor } from 'app/plugins/datasource/dashboard'; import { GrafanaQuery } from 'app/plugins/datasource/grafana/types'; import { QueryGroupOptions } from 'app/types'; import { PanelTimeRange } from '../../scene/PanelTimeRange'; -import { ShareQueryDataProvider } from '../../scene/ShareQueryDataProvider'; import { VizPanelManager } from '../VizPanelManager'; import { PanelDataPaneTabState, PanelDataPaneTab } from './types'; @@ -30,17 +28,7 @@ export class PanelDataQueriesTab extends SceneObjectBase) { - const { panel, datasource, dsSettings } = model.panelManager.useState(); - const { $data: dataObj } = panel.useState(); - const { data } = dataObj!.useState(); + const { datasource, dsSettings } = model.panelManager.useState(); + const { data } = model.panelManager.queryRunner.useState(); if (!datasource || !dsSettings || !data) { return null; @@ -154,18 +127,14 @@ function PanelDataQueriesTabRendered({ model }: SceneComponentProps - {dataObj instanceof ShareQueryDataProvider ? ( - - ) : ( - {}} - onQueriesChange={model.onQueriesChange} - onRunQueries={model.onRunQueries} - /> - )} + {}} + onQueriesChange={model.onQueriesChange} + onRunQueries={model.onRunQueries} + /> ); } diff --git a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx index 0c5c4e49bc9..1f16cbdf45e 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelEditor.tsx @@ -18,7 +18,6 @@ import { import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { DashboardScene } from '../scene/DashboardScene'; -import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; import { DashboardModelCompatibilityWrapper } from '../utils/DashboardModelCompatibilityWrapper'; import { getDashboardUrl } from '../utils/urlBuilders'; @@ -106,13 +105,6 @@ export class PanelEditor extends SceneObjectBase { const panelMngr = this.state.panelRef.resolve(); - // Remove data provider if it's a share query. For editing purposes the data provider is cloned and attached to the - // ShareQueryDataProvider when panel is in edit mode. - // TODO: Handle transformations when we get on transformations edit. - if (panelMngr.state.panel.state.$data instanceof ShareQueryDataProvider) { - panelMngr.state.panel.state.$data.setState({ $data: undefined }); - } - if (sourcePanel.parent instanceof SceneGridItem) { sourcePanel.parent.setState({ body: panelMngr.state.panel.clone() }); } @@ -154,6 +146,7 @@ export function buildPanelEditScene(dashboard: DashboardScene, panel: VizPanel): direction: 'column', primary: new SceneFlexLayout({ direction: 'column', + minHeight: 200, children: [vizPanelMgr], }), secondary: new SceneFlexItem({ diff --git a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx index dcec8ea7ec6..044bc37d202 100644 --- a/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx +++ b/public/app/features/dashboard-scene/panel-edit/VizPanelManager.tsx @@ -4,10 +4,8 @@ import { DataSourceApi, DataSourceInstanceSettings, FieldConfigSource, - LoadingState, PanelModel, filterFieldConfigOverrides, - getDefaultTimeRange, isStandardFieldProp, restoreCustomOverrideRules, } from '@grafana/data'; @@ -24,17 +22,14 @@ import { SceneDataTransformer, SceneDataProvider, } from '@grafana/scenes'; -import { DataQuery, DataSourceRef } from '@grafana/schema'; +import { DataQuery } from '@grafana/schema'; import { getPluginVersion } from 'app/features/dashboard/state/PanelModel'; import { storeLastUsedDataSourceInLocalStorage } from 'app/features/datasources/components/picker/utils'; import { updateQueries } from 'app/features/query/state/updateQueries'; -import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; -import { DASHBOARD_DATASOURCE_PLUGIN_ID, DashboardQuery } from 'app/plugins/datasource/dashboard/types'; import { GrafanaQuery } from 'app/plugins/datasource/grafana/types'; import { QueryGroupOptions } from 'app/types'; import { PanelTimeRange, PanelTimeRangeState } from '../scene/PanelTimeRange'; -import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; import { getPanelIdForVizPanel } from '../utils/utils'; interface VizPanelManagerState extends SceneObjectState { @@ -73,16 +68,7 @@ export class VizPanelManager extends SceneObjectBase { return; } - let datasourceToLoad: DataSourceRef | undefined; - - if (dataObj instanceof ShareQueryDataProvider) { - datasourceToLoad = { - uid: SHARED_DASHBOARD_QUERY, - type: DASHBOARD_DATASOURCE_PLUGIN_ID, - }; - } else { - datasourceToLoad = this.queryRunner.state.datasource; - } + let datasourceToLoad = this.queryRunner.state.datasource; if (!datasourceToLoad) { return; @@ -166,93 +152,28 @@ export class VizPanelManager extends SceneObjectBase { newSettings: DataSourceInstanceSettings, defaultQueries?: DataQuery[] | GrafanaQuery[] ) { - const { panel, dsSettings } = this.state; - const dataObj = panel.state.$data; - if (!dataObj) { - return; - } + const { dsSettings } = this.state; + const queryRunner = this.queryRunner; const currentDS = dsSettings ? await getDataSourceSrv().get({ uid: dsSettings.uid }) : undefined; const nextDS = await getDataSourceSrv().get({ uid: newSettings.uid }); - const currentQueries = []; - if (dataObj instanceof SceneQueryRunner) { - currentQueries.push(...dataObj.state.queries); - } else if (dataObj instanceof ShareQueryDataProvider) { - currentQueries.push(dataObj.state.query); - } + const currentQueries = queryRunner.state.queries; // We need to pass in newSettings.uid as well here as that can be a variable expression and we want to store that in the query model not the current ds variable value const queries = defaultQueries || (await updateQueries(nextDS, newSettings.uid, currentQueries, currentDS)); - if (dataObj instanceof SceneQueryRunner) { - // Changing to Dashboard data source - if (newSettings.uid === SHARED_DASHBOARD_QUERY) { - // Changing from one plugin to another - const sharedProvider = new ShareQueryDataProvider({ - query: queries[0], - $data: new SceneQueryRunner({ - queries: [], - }), - data: { - series: [], - state: LoadingState.NotStarted, - timeRange: getDefaultTimeRange(), - }, - }); - panel.setState({ $data: sharedProvider }); - } else { - dataObj.setState({ - datasource: { - type: newSettings.type, - uid: newSettings.uid, - }, - queries, - }); - if (defaultQueries) { - dataObj.runQueries(); - } - } - } else if (dataObj instanceof ShareQueryDataProvider && newSettings.uid !== SHARED_DASHBOARD_QUERY) { - const dataProvider = new SceneQueryRunner({ - datasource: { - type: newSettings.type, - uid: newSettings.uid, - }, - queries, - }); - panel.setState({ $data: dataProvider }); - } else if (dataObj instanceof SceneDataTransformer) { - const data = dataObj.clone(); - - let provider: SceneDataProvider = new SceneQueryRunner({ - datasource: { - type: newSettings.type, - uid: newSettings.uid, - }, - queries, - }); - - if (newSettings.uid === SHARED_DASHBOARD_QUERY) { - provider = new ShareQueryDataProvider({ - query: queries[0], - $data: new SceneQueryRunner({ - queries: [], - }), - data: { - series: [], - state: LoadingState.NotStarted, - timeRange: getDefaultTimeRange(), - }, - }); - } - - data.setState({ - $data: provider, - }); - - panel.setState({ $data: data }); + queryRunner.setState({ + datasource: { + type: newSettings.type, + uid: newSettings.uid, + }, + queries, + }); + if (defaultQueries) { + queryRunner.runQueries(); } + this.loadDataSource(); } @@ -293,19 +214,8 @@ export class VizPanelManager extends SceneObjectBase { } public changeQueries(queries: T[]) { - const dataObj = this.state.panel.state.$data; const runner = this.queryRunner; - - if (dataObj instanceof ShareQueryDataProvider && queries.length > 0) { - const dashboardQuery = queries[0] as DashboardQuery; - if (dashboardQuery.panelId) { - dataObj.setState({ - query: dashboardQuery, - }); - } - } else { - runner.setState({ queries }); - } + runner.setState({ queries }); } public inspectPanel() { @@ -321,10 +231,6 @@ export class VizPanelManager extends SceneObjectBase { get queryRunner(): SceneQueryRunner { const dataObj = this.state.panel.state.$data; - if (dataObj instanceof ShareQueryDataProvider) { - return dataObj.state.$data as SceneQueryRunner; - } - if (dataObj instanceof SceneDataTransformer) { return dataObj.state.$data as SceneQueryRunner; } diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index f8e3407681c..c12193d1c04 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -6,15 +6,7 @@ import { getTimeZone, } from '@grafana/data'; import { config, getPluginLinkExtensions, locationService } from '@grafana/runtime'; -import { - LocalValueVariable, - SceneDataTransformer, - SceneGridRow, - SceneQueryRunner, - VizPanel, - VizPanelMenu, - sceneGraph, -} from '@grafana/scenes'; +import { LocalValueVariable, SceneGridRow, VizPanel, VizPanelMenu, sceneGraph } from '@grafana/scenes'; import { DataQuery } from '@grafana/schema'; import { t } from 'app/core/internationalization'; import { PanelModel } from 'app/features/dashboard/state'; @@ -26,12 +18,11 @@ import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegratio import { ShareModal } from '../sharing/ShareModal'; import { DashboardInteractions } from '../utils/interactions'; import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders'; -import { getPanelIdForVizPanel } from '../utils/utils'; +import { getPanelIdForVizPanel, getQueryRunnerFor } from '../utils/utils'; import { DashboardScene } from './DashboardScene'; import { LibraryVizPanel } from './LibraryVizPanel'; import { VizPanelLinks } from './PanelLinks'; -import { ShareQueryDataProvider } from './ShareQueryDataProvider'; /** * Behavior is called when VizPanelMenu is activated (ie when it's opened). @@ -233,22 +224,10 @@ export function getPanelLinksBehavior(panel: PanelModel) { function createExtensionContext(panel: VizPanel, dashboard: DashboardScene): PluginExtensionPanelContext { const timeRange = sceneGraph.getTimeRange(panel); - let queryRunner = panel.state.$data; + let queryRunner = getQueryRunnerFor(panel); let targets: DataQuery[] = []; const id = getPanelIdForVizPanel(panel); - if (queryRunner instanceof SceneDataTransformer) { - queryRunner = queryRunner.state.$data; - } - - if (queryRunner instanceof SceneQueryRunner) { - targets = queryRunner.state.queries; - } - - if (queryRunner instanceof ShareQueryDataProvider) { - targets = [queryRunner.state.query]; - } - let scopedVars = {}; // Handle panel repeats scenario diff --git a/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.test.ts b/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.test.ts deleted file mode 100644 index 14c7e570505..00000000000 --- a/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.test.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { getDefaultTimeRange, LoadingState } from '@grafana/data'; -import { - SceneDataNode, - SceneFlexItem, - SceneFlexLayout, - sceneGraph, - SceneObjectBase, - SceneObjectState, -} from '@grafana/scenes'; - -import { activateFullSceneTree } from '../utils/test-utils'; -import { getVizPanelKeyForPanelId } from '../utils/utils'; - -import { ShareQueryDataProvider } from './ShareQueryDataProvider'; - -export class SceneDummyPanel extends SceneObjectBase {} - -describe('ShareQueryDataProvider', () => { - it('Should find and subscribe to another VizPanels data provider', () => { - const panel = new SceneDummyPanel({ - key: getVizPanelKeyForPanelId(2), - $data: new ShareQueryDataProvider({ - query: { refId: 'A', panelId: 1 }, - }), - }); - - const sourceData = new SceneDataNode({ - data: { - series: [], - state: LoadingState.Done, - timeRange: getDefaultTimeRange(), - structureRev: 11, - }, - }); - - const scene = new SceneFlexLayout({ - children: [ - new SceneFlexItem({ - body: new SceneDummyPanel({ - key: getVizPanelKeyForPanelId(1), - $data: sourceData, - }), - }), - new SceneFlexItem({ body: panel }), - ], - }); - - activateFullSceneTree(scene); - - expect(sceneGraph.getData(panel).state.data?.structureRev).toBe(11); - - sourceData.setState({ data: { ...sourceData.state.data!, structureRev: 12 } }); - - expect(sceneGraph.getData(panel).state.data?.structureRev).toBe(12); - }); - - it('should find and update to another VizPanels data provider when query changed', () => { - const sharedQuery = new ShareQueryDataProvider({ - query: { refId: 'A', panelId: 1 }, - }); - const panelWithSharedQuery = new SceneDummyPanel({ - key: getVizPanelKeyForPanelId(3), - $data: sharedQuery, - }); - - const sourceData1 = new SceneDataNode({ - data: { - series: [], - state: LoadingState.Done, - timeRange: getDefaultTimeRange(), - structureRev: 1, - }, - }); - - const sourceData2 = new SceneDataNode({ - data: { - series: [], - state: LoadingState.Done, - timeRange: getDefaultTimeRange(), - structureRev: 100, - }, - }); - - const sourcePanel1 = new SceneDummyPanel({ - key: getVizPanelKeyForPanelId(1), - $data: sourceData1, - }); - - const sourcePanel2 = new SceneDummyPanel({ - key: getVizPanelKeyForPanelId(2), - $data: sourceData2, - }); - - const scene = new SceneFlexLayout({ - children: [ - new SceneFlexItem({ - body: sourcePanel1, - }), - new SceneFlexItem({ - body: sourcePanel2, - }), - new SceneFlexItem({ body: panelWithSharedQuery }), - ], - }); - - activateFullSceneTree(scene); - - expect(sceneGraph.getData(panelWithSharedQuery).state.data?.structureRev).toBe(1); - - sharedQuery.setState({ - query: { - refId: 'A', - panelId: 2, - }, - }); - - expect(sceneGraph.getData(panelWithSharedQuery).state.data?.structureRev).toBe(100); - sourceData2.setState({ data: { ...sourceData2.state.data!, structureRev: 101 } }); - expect(sceneGraph.getData(panelWithSharedQuery).state.data?.structureRev).toBe(101); - }); -}); diff --git a/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.ts b/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.ts deleted file mode 100644 index 8564df69c53..00000000000 --- a/public/app/features/dashboard-scene/scene/ShareQueryDataProvider.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { Observable, ReplaySubject, Unsubscribable } from 'rxjs'; - -import { getDefaultTimeRange, LoadingState, PanelData } from '@grafana/data'; -import { - SceneDataProvider, - SceneDataProviderResult, - SceneDataState, - SceneDataTransformer, - SceneDeactivationHandler, - SceneObject, - SceneObjectBase, -} from '@grafana/scenes'; -import { DashboardQuery } from 'app/plugins/datasource/dashboard/types'; - -import { PanelEditor } from '../panel-edit/PanelEditor'; -import { getVizPanelKeyForPanelId } from '../utils/utils'; - -export interface ShareQueryDataProviderState extends SceneDataState { - query: DashboardQuery; -} - -export class ShareQueryDataProvider extends SceneObjectBase implements SceneDataProvider { - private _querySub: Unsubscribable | undefined; - private _sourceDataDeactivationHandler?: SceneDeactivationHandler; - private _results = new ReplaySubject(); - private _sourceProvider?: SceneDataProvider; - private _passContainerWidth = false; - - constructor(state: ShareQueryDataProviderState) { - super({ - data: emptyPanelData, - ...state, - }); - - this.addActivationHandler(this._onActivate); - } - - // Will detect the root of the scene and if it's a PanelEditor, it will clone the data from the source panel. - // We are doing it because the source panel's original scene (dashboard) does not exist in the edit mode. - private _setupEditMode(panelId: number, root: PanelEditor) { - const keyToFind = getVizPanelKeyForPanelId(panelId); - const source = findObjectInScene( - root.state.dashboardRef.resolve(), - (scene: SceneObject) => scene.state.key === keyToFind - ); - - if (source) { - // Indicate that when de-activated, i.e. navigating back to dashboard, the cloned$data state needs to be removed - // so that the panel on the dashboar can use data from the actual source panel. - this.setState({ - $data: source.state.$data!.clone(), - }); - } - } - - private _onActivate = () => { - const root = this.getRoot(); - if (root instanceof PanelEditor && this.state.query.panelId) { - this._setupEditMode(this.state.query.panelId, root); - } - // TODO handle changes to query model (changed panelId / withTransforms) - this.subscribeToState(this._onStateChanged); - /** - * If the panel uses a shared query, we clone the source runner and attach it as a data provider for the shared one. - * This way the source panel does not to be present in the edit scene hierarchy. - */ - - this._subscribeToSource(); - - return () => { - if (this._querySub) { - this._querySub.unsubscribe(); - } - if (this._sourceDataDeactivationHandler) { - this._sourceDataDeactivationHandler(); - this._sourceDataDeactivationHandler = undefined; - } - }; - }; - - public getResultsStream(): Observable { - return this._results; - } - - private _subscribeToSource() { - const { query } = this.state; - - if (this._querySub) { - this._querySub.unsubscribe(); - } - - if (this._sourceDataDeactivationHandler) { - this._sourceDataDeactivationHandler(); - this._sourceDataDeactivationHandler = undefined; - } - - if (this.state.$data) { - this._sourceProvider = this.state.$data; - this._passContainerWidth = true; - } else { - if (!query.panelId) { - return; - } - - const keyToFind = getVizPanelKeyForPanelId(query.panelId); - const source = findObjectInScene(this.getRoot(), (scene: SceneObject) => scene.state.key === keyToFind); - - if (!source) { - console.log('Shared dashboard query refers to a panel that does not exist in the scene'); - return; - } - - this._sourceProvider = source.state.$data; - if (!this._sourceProvider) { - console.log('No source data found for shared dashboard query'); - return; - } - } - - // If the source is not active we need to pass the container width - if (!this._sourceProvider.isActive) { - this._passContainerWidth = true; - } - - // This will activate if sourceData is part of hidden panel - // Also make sure the sourceData is not deactivated if hidden later - this._sourceDataDeactivationHandler = this._sourceProvider.activate(); - - // If source is a data transformer we might need to get the inner query runner instead depending on withTransforms option - if (this._sourceProvider instanceof SceneDataTransformer && !query.withTransforms) { - if (!this._sourceProvider.state.$data) { - throw new Error('No source inner query runner found in data transformer'); - } - this._sourceProvider = this._sourceProvider.state.$data; - } - - this._querySub = this._sourceProvider.subscribeToState((state) => { - this._results.next({ - origin: this, - data: state.data || { - state: LoadingState.Done, - series: [], - timeRange: getDefaultTimeRange(), - }, - }); - - this.setState({ data: state.data }); - }); - - // Copy the initial state - this.setState({ data: this._sourceProvider.state.data || emptyPanelData }); - } - - private _onStateChanged = (n: ShareQueryDataProviderState, p: ShareQueryDataProviderState) => { - const root = this.getRoot(); - // If the query changed, we need to find the new source panel and subscribe to it - if (n.query !== p.query && n.query.panelId) { - if (root instanceof PanelEditor) { - this._setupEditMode(n.query.panelId, root); - } - this._subscribeToSource(); - } - }; - - public setContainerWidth(width: number) { - if (this._passContainerWidth && this._sourceProvider) { - this._sourceProvider.setContainerWidth?.(width); - } - } - - public isDataReadyToDisplay() { - if (this._sourceProvider && this._sourceProvider.isDataReadyToDisplay) { - return this._sourceProvider.isDataReadyToDisplay(); - } - return false; - } -} - -export function findObjectInScene(scene: SceneObject, check: (scene: SceneObject) => boolean): SceneObject | null { - if (check(scene)) { - return scene; - } - - let found: SceneObject | null = null; - - scene.forEachChild((child) => { - let maybe = findObjectInScene(child, check); - if (maybe) { - found = maybe; - } - }); - - return found; -} - -const emptyPanelData: PanelData = { state: LoadingState.Done, series: [], timeRange: getDefaultTimeRange() }; diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts index eba7dd50d8e..66b922c2011 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts @@ -98,6 +98,7 @@ describe('sceneVariablesSetToVariables', () => { allValue: 'test-all', isMulti: true, }); + const set = new SceneVariableSet({ variables: [variable], }); diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts index ccb5f53fc6d..e4b361bccae 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts @@ -28,7 +28,6 @@ import { } from '@grafana/schema'; import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object'; import { getPanelDataFrames } from 'app/features/dashboard/components/HelpWizard/utils'; -import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { GrafanaQueryType } from 'app/plugins/datasource/grafana/types'; import { DashboardControls } from '../scene/DashboardControls'; @@ -37,7 +36,6 @@ import { LibraryVizPanel } from '../scene/LibraryVizPanel'; 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'; import { GRAFANA_DATASOURCE_REF } from './const'; @@ -231,21 +229,6 @@ function vizPanelDataToPanel( const dataProvider = vizPanel.state.$data; const panel: Pick = {}; - // 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) { @@ -257,20 +240,6 @@ function vizPanelDataToPanel( // 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; diff --git a/public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts b/public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts index 5a5d96d57ef..fb0dea65adc 100644 --- a/public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts +++ b/public/app/features/dashboard-scene/utils/DashboardModelCompatibilityWrapper.ts @@ -10,18 +10,15 @@ import { SceneGridLayout, SceneGridRow, SceneObject, - SceneQueryRunner, VizPanel, } from '@grafana/scenes'; import { DataSourceRef } from '@grafana/schema'; -import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; import { DashboardScene } from '../scene/DashboardScene'; import { LibraryVizPanel } from '../scene/LibraryVizPanel'; -import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; import { dashboardSceneGraph } from './dashboardSceneGraph'; -import { findVizPanelByKey, getPanelIdForVizPanel, getVizPanelKeyForPanelId } from './utils'; +import { findVizPanelByKey, getPanelIdForVizPanel, getQueryRunnerFor, getVizPanelKeyForPanelId } from './utils'; /** * Will move this to make it the main way we remain somewhat compatible with getDashboardSrv().getCurrent @@ -248,60 +245,17 @@ class PanelCompatibilityWrapper { } public get targets() { - if (this._vizPanel.state.$data instanceof SceneQueryRunner) { - return this._vizPanel.state.$data.state.queries; + const queryRunner = getQueryRunnerFor(this._vizPanel); + if (!queryRunner) { + return []; } - if (this._vizPanel.state.$data instanceof ShareQueryDataProvider) { - return [ - { - datasource: { - uid: SHARED_DASHBOARD_QUERY, - type: 'datasource', - }, - ...this._vizPanel.state.$data.state.query, - }, - ]; - } - - if (this._vizPanel.state.$data instanceof SceneDataTransformer) { - if (this._vizPanel.state.$data.state.$data instanceof ShareQueryDataProvider) { - return [ - { - datasource: { - uid: SHARED_DASHBOARD_QUERY, - type: 'datasource', - }, - ...(this._vizPanel.state.$data.state.$data as ShareQueryDataProvider).state.query, - }, - ]; - } - if (this._vizPanel.state.$data.state.$data instanceof SceneQueryRunner) { - return (this._vizPanel.state.$data.state.$data as SceneQueryRunner).state.queries; - } - } - - return []; + return queryRunner.state.queries; } - public get datasource(): DataSourceRef | null { - if (this._vizPanel.state.$data instanceof SceneQueryRunner) { - return this._vizPanel.state.$data.state.datasource ?? null; - } - - if (this._vizPanel.state.$data instanceof ShareQueryDataProvider) { - return { uid: SHARED_DASHBOARD_QUERY, type: 'datasource' }; - } - - if (this._vizPanel.state.$data instanceof SceneDataTransformer) { - if (this._vizPanel.state.$data.state.$data instanceof ShareQueryDataProvider) { - return { uid: SHARED_DASHBOARD_QUERY, type: 'datasource' }; - } - - return (this._vizPanel.state.$data.state.$data as SceneQueryRunner).state.datasource ?? null; - } - - return null; + public get datasource(): DataSourceRef | null | undefined { + const queryRunner = getQueryRunnerFor(this._vizPanel); + return queryRunner?.state.datasource; } public refresh() { diff --git a/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts b/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts index 12a6d721db2..c74394228e9 100644 --- a/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts +++ b/public/app/features/dashboard-scene/utils/createPanelDataProvider.ts @@ -1,9 +1,6 @@ import { config } from '@grafana/runtime'; import { SceneDataProvider, SceneDataTransformer, SceneQueryRunner } from '@grafana/scenes'; import { PanelModel } from 'app/features/dashboard/state'; -import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; - -import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider'; export function createPanelDataProvider(panel: PanelModel): SceneDataProvider | undefined { // Skip setting query runner for panels without queries @@ -18,19 +15,15 @@ export function createPanelDataProvider(panel: PanelModel): SceneDataProvider | let dataProvider: SceneDataProvider | undefined = undefined; - if (panel.datasource?.uid === SHARED_DASHBOARD_QUERY) { - dataProvider = new ShareQueryDataProvider({ query: panel.targets[0] }); - } else { - dataProvider = new SceneQueryRunner({ - datasource: panel.datasource ?? undefined, - queries: panel.targets, - maxDataPoints: panel.maxDataPoints ?? undefined, - maxDataPointsFromWidth: true, - dataLayerFilter: { - panelId: panel.id, - }, - }); - } + dataProvider = new SceneQueryRunner({ + datasource: panel.datasource ?? undefined, + queries: panel.targets, + maxDataPoints: panel.maxDataPoints ?? undefined, + maxDataPointsFromWidth: true, + dataLayerFilter: { + panelId: panel.id, + }, + }); // Wrap inner data provider in a data transformer if (panel.transformations?.length) { diff --git a/public/app/features/query/components/QueryGroup.tsx b/public/app/features/query/components/QueryGroup.tsx index 8b8aab4222c..9eb13f5c2a3 100644 --- a/public/app/features/query/components/QueryGroup.tsx +++ b/public/app/features/query/components/QueryGroup.tsx @@ -23,7 +23,7 @@ import { DataSourceModal } from 'app/features/datasources/components/picker/Data import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { dataSource as expressionDatasource } from 'app/features/expressions/ExpressionDatasource'; import { AngularDeprecationPluginNotice } from 'app/features/plugins/angularDeprecation/AngularDeprecationPluginNotice'; -import { DashboardQueryEditor, isSharedDashboardQuery } from 'app/plugins/datasource/dashboard'; +import { isSharedDashboardQuery } from 'app/plugins/datasource/dashboard'; import { GrafanaQuery } from 'app/plugins/datasource/grafana/types'; import { QueryGroupOptions } from 'app/types'; @@ -252,16 +252,16 @@ export class QueryGroup extends PureComponent { renderQueries(dsSettings: DataSourceInstanceSettings) { const { onRunQueries } = this.props; const { data, queries } = this.state; - if (isSharedDashboardQuery(dsSettings.name)) { - return ( - - ); - } + // if (isSharedDashboardQuery(dsSettings.name)) { + // return ( + // + // ); + // } return (
diff --git a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx index 8b2f0c435ee..6fc9e91593d 100644 --- a/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx +++ b/public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx @@ -4,7 +4,7 @@ import pluralize from 'pluralize'; import React, { useCallback, useMemo } from 'react'; import { useAsync } from 'react-use'; -import { DataQuery, GrafanaTheme2, PanelData, SelectableValue, DataTopic } from '@grafana/data'; +import { DataQuery, GrafanaTheme2, SelectableValue, DataTopic, QueryEditorProps } from '@grafana/data'; import { Card, Field, @@ -22,27 +22,22 @@ import { PanelModel } from 'app/features/dashboard/state'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { filterPanelDataToQuery } from 'app/features/query/components/QueryEditorRow'; +import { DashboardDatasource } from './datasource'; import { DashboardQuery, ResultInfo, SHARED_DASHBOARD_QUERY } from './types'; function getQueryDisplayText(query: DataQuery): string { return JSON.stringify(query); } -interface Props { - queries: DataQuery[]; - panelData: PanelData; - onChange: (queries: DataQuery[]) => void; - onRunQueries?: () => void; -} +interface Props extends QueryEditorProps {} const topics = [ { label: 'All data', value: false }, { label: 'Annotations', value: true, description: 'Include annotations as regular data' }, ]; -export function DashboardQueryEditor({ panelData, queries, onChange, onRunQueries }: Props) { +export function DashboardQueryEditor({ data, query, onChange, onRunQuery }: Props) { const { value: defaultDatasource } = useAsync(() => getDatasourceSrv().get()); - const query = queries[0] as DashboardQuery; const panel = useMemo(() => { const dashboard = getDashboardSrv().getCurrent(); @@ -50,7 +45,7 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie }, [query.panelId]); const { value: results, loading: loadingResults } = useAsync(async (): Promise => { - if (!panel) { + if (!panel || !data) { return []; } const mainDS = await getDatasourceSrv().get(panel.datasource); @@ -58,7 +53,7 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie panel.targets.map(async (query) => { const ds = query.datasource ? await getDatasourceSrv().get(query.datasource) : mainDS; const fmt = ds.getQueryDisplayText || getQueryDisplayText; - const queryData = filterPanelDataToQuery(panelData, query.refId) ?? panelData; + const queryData = filterPanelDataToQuery(data, query.refId) ?? data; return { refId: query.refId, query: fmt(query), @@ -69,16 +64,14 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie }; }) ); - }, [panelData, panel]); + }, [data, panel]); const onUpdateQuery = useCallback( (query: DashboardQuery) => { - onChange([query]); - if (onRunQueries) { - onRunQueries(); - } + onChange(query); + onRunQuery(); }, - [onChange, onRunQueries] + [onChange, onRunQuery] ); const onPanelChanged = useCallback( diff --git a/public/app/plugins/datasource/dashboard/datasource.ts b/public/app/plugins/datasource/dashboard/datasource.ts index 96fcc13c6f6..e3ddf75cef7 100644 --- a/public/app/plugins/datasource/dashboard/datasource.ts +++ b/public/app/plugins/datasource/dashboard/datasource.ts @@ -1,3 +1,5 @@ +import { Observable, map, of } from 'rxjs'; + import { DataSourceApi, DataQueryRequest, @@ -5,6 +7,9 @@ import { DataSourceInstanceSettings, TestDataSourceResponse, } from '@grafana/data'; +import { SceneDataTransformer, SceneObject } from '@grafana/scenes'; +import { PanelEditor } from 'app/features/dashboard-scene/panel-edit/PanelEditor'; +import { findVizPanelByKey, getVizPanelKeyForPanelId } from 'app/features/dashboard-scene/utils/utils'; import { DashboardQuery } from './types'; @@ -20,8 +25,65 @@ export class DashboardDatasource extends DataSourceApi { return `Dashboard Reference: ${query.panelId}`; } - query(options: DataQueryRequest): Promise { - return Promise.reject('This should not be called directly'); + query(options: DataQueryRequest): Observable { + const scene: SceneObject | undefined = options.scopedVars?.__sceneObject?.value; + + if (!scene) { + throw new Error('Can only be called from a scene'); + } + + const query = options.targets[0]; + if (!query) { + return of({ data: [] }); + } + + const panelId = query.panelId; + + if (!panelId) { + return of({ data: [] }); + } + + let sourcePanel = this.findSourcePanel(scene, panelId); + + if (!sourcePanel) { + return of({ data: [], error: { message: 'Could not find source panel' } }); + } + + let sourceDataProvider = sourcePanel?.state.$data; + + if (query.withTransforms && sourceDataProvider instanceof SceneDataTransformer) { + sourceDataProvider = sourceDataProvider.state.$data!; + } + + if (!sourceDataProvider || !sourceDataProvider.getResultsStream) { + return of({ data: [] }); + } + + if (!sourceDataProvider?.isActive) { + sourceDataProvider?.activate(); + sourceDataProvider.setContainerWidth!(500); + } + + return sourceDataProvider.getResultsStream().pipe( + map((result) => { + return { + data: result.data.series, + state: result.data.state, + errors: result.data.errors, + error: result.data.error, + }; + }) + ); + } + + private findSourcePanel(scene: SceneObject, panelId: number) { + let sceneToSearch = scene.getRoot(); + + if (sceneToSearch instanceof PanelEditor) { + sceneToSearch = sceneToSearch.state.dashboardRef.resolve(); + } + + return findVizPanelByKey(sceneToSearch, getVizPanelKeyForPanelId(panelId)); } testDatasource(): Promise { diff --git a/public/app/plugins/datasource/dashboard/module.ts b/public/app/plugins/datasource/dashboard/module.ts index 1fa8553046e..c9f0cc591ec 100644 --- a/public/app/plugins/datasource/dashboard/module.ts +++ b/public/app/plugins/datasource/dashboard/module.ts @@ -1,5 +1,6 @@ import { DataSourcePlugin } from '@grafana/data'; +import { DashboardQueryEditor } from './DashboardQueryEditor'; import { DashboardDatasource } from './datasource'; -export const plugin = new DataSourcePlugin(DashboardDatasource); +export const plugin = new DataSourcePlugin(DashboardDatasource).setQueryEditor(DashboardQueryEditor);