Dashboard Scene: Fix issue for Dashboard Datasource and library panel (#93220)

This commit is contained in:
Alexa V
2024-09-12 16:49:22 +02:00
committed by GitHub
parent efe62086f9
commit 22fe575925
3 changed files with 130 additions and 11 deletions
@@ -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);
});
});
});
@@ -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<DashboardDatas
}
private _activationHandler() {
const queryRunner = this.parent;
const dashboardDsQueryRunner = this.parent;
let libraryPanelSub: Unsubscribable;
let dashboard: DashboardScene;
if (!(queryRunner instanceof SceneQueryRunner)) {
if (!(dashboardDsQueryRunner instanceof SceneQueryRunner)) {
throw new Error('DashboardDatasourceBehaviour must be attached to a SceneQueryRunner');
}
if (queryRunner.state.datasource?.uid !== SHARED_DASHBOARD_QUERY) {
if (dashboardDsQueryRunner.state.datasource?.uid !== SHARED_DASHBOARD_QUERY) {
return;
}
try {
dashboard = getDashboardSceneFor(queryRunner);
dashboard = getDashboardSceneFor(dashboardDsQueryRunner);
} catch {
return;
}
const dashboardQuery = queryRunner.state.queries.find((query) => 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();
}
}
}
@@ -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;