grafana/public/app/features/scenes/dashboard/ShareQueryDataProvider.ts

104 lines
2.8 KiB
TypeScript

import { Unsubscribable } from 'rxjs';
import {
SceneDataProvider,
SceneDataState,
SceneDataTransformer,
SceneDeactivationHandler,
SceneObject,
SceneObjectBase,
} from '@grafana/scenes';
import { DashboardQuery } from 'app/plugins/datasource/dashboard/types';
import { getVizPanelKeyForPanelId } from './utils';
export interface ShareQueryDataProviderState extends SceneDataState {
query: DashboardQuery;
}
export class ShareQueryDataProvider extends SceneObjectBase<ShareQueryDataProviderState> implements SceneDataProvider {
private _querySub: Unsubscribable | undefined;
private _sourceDataDeactivationHandler?: SceneDeactivationHandler;
constructor(state: ShareQueryDataProviderState) {
super(state);
this.addActivationHandler(() => {
// TODO handle changes to query model (changed panelId / withTransforms)
//this.subscribeToState(this._onStateChanged);
this._subscribeToSource();
return () => {
if (this._querySub) {
this._querySub.unsubscribe();
}
if (this._sourceDataDeactivationHandler) {
this._sourceDataDeactivationHandler();
}
};
});
}
private _subscribeToSource() {
const { query } = this.state;
if (this._querySub) {
this._querySub.unsubscribe();
}
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;
}
let sourceData = source.state.$data;
if (!sourceData) {
console.log('No source data found for shared dashboard query');
return;
}
// This will activate if sourceData is part of hidden panel
// Also make sure the sourceData is not deactivated if hidden later
this._sourceDataDeactivationHandler = sourceData.activate();
if (sourceData instanceof SceneDataTransformer) {
if (!query.withTransforms) {
if (!sourceData.state.$data) {
throw new Error('No source inner query runner found in data transformer');
}
sourceData = sourceData.state.$data;
}
}
this._querySub = sourceData.subscribeToState((state) => this.setState({ data: state.data }));
// Copy the initial state
this.setState({ data: sourceData.state.data });
}
}
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;
}