Inspector: support custom metadata display (#20854)

This commit is contained in:
Ryan McKinley
2020-01-09 22:59:23 -08:00
committed by GitHub
parent 0bf9e9bc28
commit bf18704490
9 changed files with 360 additions and 18 deletions

View File

@@ -1,17 +1,88 @@
// Libraries
import React, { PureComponent } from 'react';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { Drawer, JSONFormatter } from '@grafana/ui';
import { css } from 'emotion';
import { getLocationSrv } from '@grafana/runtime';
import { JSONFormatter, Drawer, Select, Table } from '@grafana/ui';
import { getLocationSrv, getDataSourceSrv } from '@grafana/runtime';
import { DataFrame, DataSourceApi, SelectableValue, applyFieldOverrides } from '@grafana/data';
import { config } from 'app/core/config';
interface Props {
dashboard: DashboardModel;
panel: PanelModel;
}
export class PanelInspector extends PureComponent<Props> {
enum InspectTab {
Data = 'data',
Raw = 'raw',
Issue = 'issue',
Meta = 'meta', // When result metadata exists
}
interface State {
// The last raw response
last?: any;
// Data frem the last response
data: DataFrame[];
// The selected data frame
selected: number;
// The Selected Tab
tab: InspectTab;
// If the datasource supports custom metadata
metaDS?: DataSourceApi;
}
export class PanelInspector extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
data: [],
selected: 0,
tab: InspectTab.Data,
};
}
async componentDidMount() {
const { panel } = this.props;
if (!panel) {
this.onDismiss(); // Try to close the component
return;
}
// TODO? should we get the result with an observable once?
const lastResult = (panel.getQueryRunner() as any).lastResult;
if (!lastResult) {
this.onDismiss(); // Usually opened from refresh?
return;
}
// Find the first DataSource wanting to show custom metadata
let metaDS: DataSourceApi;
const data = lastResult?.series as DataFrame[];
if (data) {
for (const frame of data) {
const key = frame.meta?.datasource;
if (key) {
const ds = await getDataSourceSrv().get(key);
if (ds && ds.components.MetadataInspector) {
metaDS = ds;
break;
}
}
}
}
// Set last result, but no metadata inspector
this.setState({
last: lastResult,
data,
metaDS,
});
}
onDismiss = () => {
getLocationSrv().update({
query: { inspect: null },
@@ -19,24 +90,98 @@ export class PanelInspector extends PureComponent<Props> {
});
};
onSelectTab = (item: SelectableValue<InspectTab>) => {
this.setState({ tab: item.value || InspectTab.Data });
};
onSelectedFrameChanged = (item: SelectableValue<number>) => {
this.setState({ selected: item.value || 0 });
};
renderMetadataInspector() {
const { metaDS, data } = this.state;
if (!metaDS || !metaDS.components?.MetadataInspector) {
return <div>No Metadata Inspector</div>;
}
return <metaDS.components.MetadataInspector datasource={metaDS} data={data} />;
}
renderDataTab() {
const { data, selected } = this.state;
if (!data || !data.length) {
return <div>No Data</div>;
}
const choices = data.map((frame, index) => {
return {
value: index,
label: `${frame.name} (${index})`,
};
});
// Apply dummy styles
const processed = applyFieldOverrides({
data,
theme: config.theme,
fieldOptions: { defaults: {}, overrides: [] },
replaceVariables: (value: string) => {
return value;
},
});
return (
<div>
{choices.length > 1 && (
<div>
<Select
options={choices}
value={choices.find(t => t.value === selected)}
onChange={this.onSelectedFrameChanged}
/>
</div>
)}
<div style={{ border: '1px solid #666' }}>
<Table width={330} height={400} data={processed[selected]} />
</div>
</div>
);
}
renderIssueTab() {
return <div>TODO: show issue form</div>;
}
render() {
const { panel } = this.props;
const { last, tab } = this.state;
if (!panel) {
this.onDismiss(); // Try to close the component
return null;
}
const bodyStyle = css`
max-height: 70vh;
overflow-y: scroll;
`;
// TODO? should we get the result with an observable once?
const data = (panel.getQueryRunner() as any).lastResult;
const tabs = [
{ label: 'Data', value: InspectTab.Data },
{ label: 'Issue', value: InspectTab.Issue },
{ label: 'Raw JSON', value: InspectTab.Raw },
];
if (this.state.metaDS) {
tabs.push({ label: 'Meta Data', value: InspectTab.Meta });
}
return (
<Drawer title={panel.title} onClose={this.onDismiss}>
<div className={bodyStyle}>
<JSONFormatter json={data} open={2} />
</div>
<Select options={tabs} value={tabs.find(t => t.value === tab)} onChange={this.onSelectTab} />
{tab === InspectTab.Data && this.renderDataTab()}
{tab === InspectTab.Meta && this.renderMetadataInspector()}
{tab === InspectTab.Issue && this.renderIssueTab()}
{tab === InspectTab.Raw && (
<div>
<JSONFormatter json={last} open={2} />
</div>
)}
</Drawer>
);
}