DashboardScene: Inspect / query tab (#74795)

This commit is contained in:
Torkel Ödegaard 2023-09-14 12:45:13 +02:00 committed by GitHub
parent 8874a8d398
commit 05f01dee0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 63 deletions

View File

@ -125,7 +125,7 @@ export class InspectJsonTab extends SceneObjectBase<InspectJsonTabState> {
panel_type_changed: panel.state.pluginId !== panelModel.type,
panel_id_changed: getPanelIdForVizPanel(panel) !== panelModel.id,
panel_grid_pos_changed: hasGridPosChanged(panel.parent.state, newState),
panel_targets_changed: hasQueriesChanged(getQueryRunnerFor(panel), getQueryRunnerFor(gridItem.state.body)),
panel_targets_changed: hasQueriesChanged(getQueryRunnerFor(panel), getQueryRunnerFor(newState.$data)),
});
};
@ -218,7 +218,6 @@ function getJsonText(show: ShowContent, panel: VizPanel): string {
case 'data-frames': {
reportPanelInspectInteraction(InspectTab.JSON, 'dataFrame');
const dataProvider = sceneGraph.getData(panel);
if (dataProvider.state.data) {

View File

@ -0,0 +1,47 @@
import React from 'react';
import {
SceneComponentProps,
sceneGraph,
SceneObjectBase,
SceneObjectState,
SceneObjectRef,
VizPanel,
} from '@grafana/scenes';
import { t } from 'app/core/internationalization';
import { QueryInspector } from 'app/features/inspector/QueryInspector';
import { InspectTab } from 'app/features/inspector/types';
import { getQueryRunnerFor } from '../utils/utils';
export interface InspectQueryTabState extends SceneObjectState {
panelRef: SceneObjectRef<VizPanel>;
}
export class InspectQueryTab extends SceneObjectBase<InspectQueryTabState> {
public getTabLabel() {
return t('dashboard.inspect.query-tab', 'Query');
}
public getTabValue() {
return InspectTab.Query;
}
public onRefreshQuery = () => {
const queryRunner = getQueryRunnerFor(this.state.panelRef.resolve());
if (queryRunner) {
queryRunner.runQueries();
}
};
static Component = ({ model }: SceneComponentProps<InspectQueryTab>) => {
const data = sceneGraph.getData(model.state.panelRef.resolve()).useState();
if (!data.data) {
return null;
}
return <QueryInspector data={data.data} onRefreshQuery={model.onRefreshQuery} />;
};
}

View File

@ -16,6 +16,7 @@ import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor
import { InspectDataTab } from './InspectDataTab';
import { InspectJsonTab } from './InspectJsonTab';
import { InspectQueryTab } from './InspectQueryTab';
import { InspectStatsTab } from './InspectStatsTab';
import { SceneInspectTab } from './types';
@ -56,6 +57,7 @@ export class PanelInspectDrawer extends SceneObjectBase<PanelInspectDrawerState>
if (supportsDataQuery(plugin)) {
tabs.push(new InspectDataTab({ panelRef }));
tabs.push(new InspectStatsTab({ panelRef }));
tabs.push(new InspectQueryTab({ panelRef }));
}
tabs.push(new InspectJsonTab({ panelRef, onClose: this.onClose }));

View File

@ -109,9 +109,7 @@ export const InspectContent = ({
)}
{activeTab === InspectTab.Error && <InspectErrorTab errors={errors} />}
{data && activeTab === InspectTab.Stats && <InspectStatsTab data={data} timeZone={dashboard.getTimezone()} />}
{data && activeTab === InspectTab.Query && (
<QueryInspector panel={panel} data={data.series} onRefreshQuery={() => panel.refresh()} />
)}
{data && activeTab === InspectTab.Query && <QueryInspector data={data} onRefreshQuery={() => panel.refresh()} />}
</Drawer>
);
};

View File

@ -34,7 +34,7 @@ const PanelInspectorUnconnected = ({ panel, dashboard, plugin }: Props) => {
});
const location = useLocation();
const { data, isLoading, error } = usePanelLatestData(panel, dataOptions, true);
const { data, isLoading, error } = usePanelLatestData(panel, dataOptions, false);
const metaDs = useDatasourceMetadata(data);
const tabs = useInspectTabs(panel, dashboard, plugin, error, metaDs);
const defaultTab = new URLSearchParams(location.search).get('inspectTab') as InspectTab;

View File

@ -64,7 +64,7 @@ export const usePanelLatestData = (
return {
data: latestData,
error: latestData && latestData.error,
isLoading: latestData ? latestData.state === LoadingState.Loading : true,
isLoading: latestData?.state === LoadingState.Loading,
hasSeries: latestData ? !!latestData.series : false,
};
};

View File

@ -35,7 +35,6 @@ jest.mock('@grafana/runtime', () => ({
const setup = (propOverrides = {}) => {
const props: ExploreQueryInspectorProps = {
loading: false,
width: 100,
exploreId: 'left',
onClose: jest.fn(),

View File

@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { CoreApp, TimeZone } from '@grafana/data';
import { CoreApp, LoadingState, TimeZone } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime/src';
import { TabbedContainer, TabConfig } from '@grafana/ui';
import { ExploreDrawer } from 'app/features/explore/ExploreDrawer';
@ -12,7 +12,7 @@ import { InspectStatsTab } from 'app/features/inspector/InspectStatsTab';
import { QueryInspector } from 'app/features/inspector/QueryInspector';
import { StoreState, ExploreItemState } from 'app/types';
import { runQueries, selectIsWaitingForData } from './state/query';
import { runQueries } from './state/query';
interface DispatchProps {
width: number;
@ -24,7 +24,7 @@ interface DispatchProps {
type Props = DispatchProps & ConnectedProps<typeof connector>;
export function ExploreQueryInspector(props: Props) {
const { loading, width, onClose, queryResponse, timeZone } = props;
const { width, onClose, queryResponse, timeZone } = props;
const dataFrames = queryResponse?.series || [];
let errors = queryResponse?.errors;
if (!errors?.length && queryResponse?.error) {
@ -57,7 +57,7 @@ export function ExploreQueryInspector(props: Props) {
<InspectDataTab
data={dataFrames}
dataName={'Explore'}
isLoading={loading}
isLoading={queryResponse.state === LoadingState.Loading}
options={{ withTransforms: false, withFieldConfig: false }}
timeZone={timeZone}
app={CoreApp.Explore}
@ -70,7 +70,7 @@ export function ExploreQueryInspector(props: Props) {
value: 'query',
icon: 'info-circle',
content: (
<QueryInspector data={dataFrames} onRefreshQuery={() => props.runQueries({ exploreId: props.exploreId })} />
<QueryInspector data={queryResponse} onRefreshQuery={() => props.runQueries({ exploreId: props.exploreId })} />
),
};
@ -97,7 +97,6 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string }
const { queryResponse } = item;
return {
loading: selectIsWaitingForData(exploreId)(state),
queryResponse,
};
}

View File

@ -2,22 +2,15 @@ import { css } from '@emotion/css';
import React, { PureComponent } from 'react';
import { Subscription } from 'rxjs';
import { DataFrame } from '@grafana/data';
import { LoadingState, PanelData } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Stack } from '@grafana/experimental';
import { config, RefreshEvent } from '@grafana/runtime';
import { config } from '@grafana/runtime';
import { Button, ClipboardButton, JSONFormatter, LoadingPlaceholder } from '@grafana/ui';
import { backendSrv } from 'app/core/services/backend_srv';
import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor/utils';
import { PanelModel } from 'app/features/dashboard/state';
import { getPanelInspectorStyles } from './styles';
interface DsQuery {
isLoading: boolean;
response: {};
}
interface ExecutedQueryInfo {
refId: string;
query: string;
@ -26,16 +19,15 @@ interface ExecutedQueryInfo {
}
interface Props {
data: DataFrame[];
data: PanelData;
onRefreshQuery: () => void;
panel?: PanelModel;
}
interface State {
allNodesExpanded: boolean | null;
isMocking: boolean;
mockedResponse: string;
dsQuery: DsQuery;
response: {};
executedQueries: ExecutedQueryInfo[];
}
@ -50,26 +42,16 @@ export class QueryInspector extends PureComponent<Props, State> {
allNodesExpanded: null,
isMocking: false,
mockedResponse: '',
dsQuery: {
isLoading: false,
response: {},
},
response: {},
};
}
componentDidMount() {
const { panel } = this.props;
this.subs.add(
backendSrv.getInspectorStream().subscribe({
next: (response) => this.onDataSourceResponse(response),
})
);
if (panel) {
this.subs.add(panel.events.subscribe(RefreshEvent, this.onPanelRefresh));
this.updateQueryList();
}
}
componentDidUpdate(oldProps: Props) {
@ -83,12 +65,13 @@ export class QueryInspector extends PureComponent<Props, State> {
*/
updateQueryList() {
const { data } = this.props;
const frames = data.series;
const executedQueries: ExecutedQueryInfo[] = [];
if (data?.length) {
if (frames?.length) {
let last: ExecutedQueryInfo | undefined = undefined;
data.forEach((frame, idx) => {
frames.forEach((frame, idx) => {
const query = frame.meta?.executedQueryString;
if (query) {
@ -117,16 +100,6 @@ export class QueryInspector extends PureComponent<Props, State> {
this.subs.unsubscribe();
}
onPanelRefresh = () => {
this.setState((prevState) => ({
...prevState,
dsQuery: {
isLoading: true,
response: {},
},
}));
};
onDataSourceResponse(response: any) {
// ignore silent requests
if (response.config?.hideFromInspector) {
@ -168,13 +141,9 @@ export class QueryInspector extends PureComponent<Props, State> {
delete response.$$config;
}
this.setState((prevState) => ({
...prevState,
dsQuery: {
isLoading: false,
response: response,
},
}));
this.setState({
response: response,
});
}
setFormattedJson = (formattedJson: any) => {
@ -240,16 +209,12 @@ export class QueryInspector extends PureComponent<Props, State> {
}
render() {
const { allNodesExpanded, executedQueries } = this.state;
const { panel, onRefreshQuery } = this.props;
const { response, isLoading } = this.state.dsQuery;
const { allNodesExpanded, executedQueries, response } = this.state;
const { onRefreshQuery, data } = this.props;
const openNodes = this.getNrOfOpenNodes();
const styles = getPanelInspectorStyles();
const haveData = Object.keys(response).length > 0;
if (panel && !supportsDataQuery(panel.plugin)) {
return null;
}
const isLoading = data.state === LoadingState.Loading;
return (
<div className={styles.wrap}>