From 22fe5759252c5cc1311f6d31bee819000a50fb3b Mon Sep 17 00:00:00 2001 From: Alexa V <239999+axelavargas@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:49:22 +0200 Subject: [PATCH] Dashboard Scene: Fix issue for Dashboard Datasource and library panel (#93220) --- .../DashboardDatasourceBehaviour.test.tsx | 81 +++++++++++++++++++ .../scene/DashboardDatasourceBehaviour.tsx | 58 ++++++++++--- .../scene/LibraryPanelBehavior.tsx | 2 +- 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.test.tsx b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.test.tsx index 4f6c19b4860..ce5d1f04606 100644 --- a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.test.tsx @@ -569,6 +569,87 @@ describe('DashboardDatasourceBehaviour', () => { expect(spy).toHaveBeenCalledTimes(1); }); + + it('should wait for library panel to load before running queries', async () => { + const libPanelBehavior = new LibraryPanelBehavior({ + isLoaded: false, + title: 'Panel title', + uid: 'fdcvggvfy2qdca', + name: 'My Library Panel', + _loadedPanel: undefined, + }); + + const sourcePanel = new VizPanel({ + key: 'panel-1', + title: 'Panel A', + pluginId: 'table', + $behaviors: [libPanelBehavior], + $data: new SceneQueryRunner({ + datasource: { uid: 'grafana' }, + queries: [{ refId: 'A', queryType: 'randomWalk' }], + }), + }); + + // query references inexistent panel + const dashboardDSPanel = new VizPanel({ + title: 'Panel B', + pluginId: 'table', + key: 'panel-2', + $data: new SceneQueryRunner({ + datasource: { uid: SHARED_DASHBOARD_QUERY }, + queries: [{ refId: 'A', panelId: 1 }], + $behaviors: [new DashboardDatasourceBehaviour({})], + }), + }); + + const scene = new DashboardScene({ + title: 'hello', + uid: 'dash-1', + meta: { + canEdit: true, + }, + body: new SceneGridLayout({ + children: [ + new DashboardGridItem({ + key: 'griditem-1', + x: 0, + y: 0, + width: 10, + height: 12, + body: sourcePanel, + }), + new DashboardGridItem({ + key: 'griditem-2', + x: 0, + y: 0, + width: 10, + height: 12, + body: dashboardDSPanel, + }), + ], + }), + }); + + activateFullSceneTree(scene); + + // spy on runQueries + const spyRunQueries = jest.spyOn(dashboardDSPanel.state.$data as SceneQueryRunner, 'runQueries'); + + await new Promise((r) => setTimeout(r, 1)); + + expect(spyRunQueries).not.toHaveBeenCalled(); + + // Simulate library panel being loaded + libPanelBehavior.setState({ + isLoaded: true, + title: 'Panel title', + uid: 'fdcvggvfy2qdca', + name: 'My Library Panel', + _loadedPanel: undefined, + }); + + expect(spyRunQueries).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx index 05880cecc19..59e992a91a1 100644 --- a/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardDatasourceBehaviour.tsx @@ -1,9 +1,18 @@ +import { Unsubscribable } from 'rxjs'; + import { SceneObjectBase, SceneObjectState, SceneQueryRunner, VizPanel } from '@grafana/scenes'; import { SHARED_DASHBOARD_QUERY } from 'app/plugins/datasource/dashboard'; -import { findVizPanelByKey, getDashboardSceneFor, getQueryRunnerFor, getVizPanelKeyForPanelId } from '../utils/utils'; +import { + findVizPanelByKey, + getDashboardSceneFor, + getLibraryPanelBehavior, + getQueryRunnerFor, + getVizPanelKeyForPanelId, +} from '../utils/utils'; import { DashboardScene } from './DashboardScene'; +import { LibraryPanelBehaviorState } from './LibraryPanelBehavior'; interface DashboardDatasourceBehaviourState extends SceneObjectState {} @@ -16,48 +25,77 @@ export class DashboardDatasourceBehaviour extends SceneObjectBase query.panelId !== undefined); + const dashboardQuery = dashboardDsQueryRunner.state.queries.find((query) => query.panelId !== undefined); if (!dashboardQuery) { return; } + // find the source panel referenced in the the dashboard ds query const panelId = dashboardQuery.panelId; const vizKey = getVizPanelKeyForPanelId(panelId); - const panel = findVizPanelByKey(dashboard, vizKey); + const sourcePanel = findVizPanelByKey(dashboard, vizKey); - if (!(panel instanceof VizPanel)) { + if (!(sourcePanel instanceof VizPanel)) { return; } - const sourcePanelQueryRunner = getQueryRunnerFor(panel); + //check if the source panel is a library panel and wait for it to load + const libraryPanelBehaviour = getLibraryPanelBehavior(sourcePanel); + if (libraryPanelBehaviour && !libraryPanelBehaviour.state.isLoaded) { + libraryPanelSub = libraryPanelBehaviour.subscribeToState((newLibPanel) => { + this.handleLibPanelStateUpdates(newLibPanel, dashboardDsQueryRunner, sourcePanel); + }); + return; + } + + const sourcePanelQueryRunner = getQueryRunnerFor(sourcePanel); if (!sourcePanelQueryRunner) { throw new Error('Could not find SceneQueryRunner for panel'); } if (this.prevRequestId && this.prevRequestId !== sourcePanelQueryRunner.state.data?.request?.requestId) { - queryRunner.runQueries(); + dashboardDsQueryRunner.runQueries(); } return () => { this.prevRequestId = sourcePanelQueryRunner?.state.data?.request?.requestId; + if (libraryPanelSub) { + libraryPanelSub.unsubscribe(); + } }; } + + private handleLibPanelStateUpdates( + newLibPanel: LibraryPanelBehaviorState, + dashboardDsQueryRunner: SceneQueryRunner, + sourcePanel: VizPanel + ) { + if (newLibPanel && newLibPanel?.isLoaded) { + const libPanelQueryRunner = getQueryRunnerFor(sourcePanel); + + if (!(libPanelQueryRunner instanceof SceneQueryRunner)) { + throw new Error('Could not find SceneQueryRunner for library panel'); + } + dashboardDsQueryRunner.runQueries(); + } + } } diff --git a/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx b/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx index 501f38e527f..d3848d1fccf 100644 --- a/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/LibraryPanelBehavior.tsx @@ -10,7 +10,7 @@ import { createPanelDataProvider } from '../utils/createPanelDataProvider'; import { DashboardGridItem } from './DashboardGridItem'; -interface LibraryPanelBehaviorState extends SceneObjectState { +export interface LibraryPanelBehaviorState extends SceneObjectState { // Library panels use title from dashboard JSON's panel model, not from library panel definition, hence we pass it. title?: string; uid: string;