Skip to content

Commit

Permalink
Inspect: Allow showing data without transformations and field config …
Browse files Browse the repository at this point in the history
…is applied (#24314)

* Inspect: Should not subscribe to transformed data

* PQR- allow controll whether or not field overrides and transformations should be applied

* UI for inspector data options

* fix

* Null check fix

* Update public/app/features/dashboard/components/Inspector/InspectDataTab.tsx

* Update public/app/features/dashboard/components/Inspector/InspectDataTab.tsx

* Apply transformations by default

* Update panel inspect docs

* Fix apply overrides

* Apply time formatting in panel inspect

* fix ts

* Post review update

* Update docs/sources/panels/inspect-panel.md

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

* lazy numbering

* fix ts

* Renames

* Renames 2

* Layout update

* Run shared request without field config

* Minor details

* fix ts

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
  • Loading branch information
3 people committed May 13, 2020
1 parent 9e24c09 commit f23ecc4
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 60 deletions.
12 changes: 8 additions & 4 deletions docs/sources/panels/inspect-panel.md
Expand Up @@ -19,7 +19,7 @@ The panel inspector displays Inspect: <NameOfPanelBeingInspected> at the top of

The panel inspector consists of four tabs:

* **Data tab -** Shows the raw data returned by the query with transformations applied. Field options such as overrides and value mappings are not applied.
* **Data tab -** Shows the raw data returned by the query with transformations applied. Field options such as overrides and value mappings are not applied by default.
* **Stats tab -** Shows how long your query takes and how much it returns.
* **JSON tab -** Allows you to view and copy the panel JSON, panel data JSON, and data frame structure JSON. This is useful if you are provisioning or administering Grafana.
* **Query tab -** Shows you the requests to the server sent when Grafana queries the data source.
Expand All @@ -42,14 +42,18 @@ The panel inspector pane opens on the right side of the screen.

### Inspect raw query results

View raw query results in a table. This is the data returned by the query with transformations applied and before the panel applies field configurations or overrides.
View raw query results in a table. This is the data returned by the query with transformations applied and before the panel applies field options or field option overrides.

1. Open the panel inspector and then click the **Data** tab or in the panel menu click **Inspect > Data**.

2. If your panel contains multiple queries or queries multiple nodes, then you have additional options.
1. If your panel contains multiple queries or queries multiple nodes, then you have additional options.
* **Select result -** Choose which result set data you want to view.
* **Transform data**
* **Join by time -** View raw data from all your queries at once, one result set per column. Click a column heading to reorder the data.

View raw query results in a table with field options and options overrides applied:
1. Open the **Data** tab in panel inspector.
1. Click on **Data display options** above the table.
1. Click on **Apply field configuration** toggle button.

### Download raw query results as CSV

Expand Down
4 changes: 2 additions & 2 deletions packages/grafana-ui/src/components/Layout/Layout.tsx
Expand Up @@ -136,8 +136,8 @@ const getStyles = stylesFactory(
align-items: ${align};
&:last-child {
margin-bottom: 0;
margin-right: 0;
margin-bottom: ${orientation === Orientation.Vertical && 0};
margin-right: ${orientation === Orientation.Horizontal && 0};
}
`,
};
Expand Down
8 changes: 2 additions & 6 deletions packages/grafana-ui/src/components/Table/DefaultCell.tsx
Expand Up @@ -7,18 +7,14 @@ export const DefaultCell: FC<TableCellProps> = props => {
const { field, cell, tableStyles, row } = props;
let link: LinkModel<any> | undefined;

if (!field.display) {
return null;
}

const displayValue = field.display(cell.value);
const displayValue = field.display ? field.display(cell.value) : cell.value;

if (field.getLinks) {
link = field.getLinks({
valueRowIndex: row.index,
})[0];
}
const value = formattedValueToString(displayValue);
const value = field.display ? formattedValueToString(displayValue) : displayValue;

return (
<div className={tableStyles.tableCell}>
Expand Down
104 changes: 87 additions & 17 deletions public/app/features/dashboard/components/Inspector/InspectDataTab.tsx
Expand Up @@ -8,18 +8,27 @@ import {
transformDataFrame,
getFrameDisplayName,
} from '@grafana/data';
import { Button, Field, Icon, Select, Table } from '@grafana/ui';
import { Button, Field, Icon, LegacyForms, Select, Table } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
import AutoSizer from 'react-virtualized-auto-sizer';

import { getPanelInspectorStyles } from './styles';
import { config } from 'app/core/config';
import { saveAs } from 'file-saver';
import { cx } from 'emotion';
import { css, cx } from 'emotion';
import { GetDataOptions } from '../../state/PanelQueryRunner';
import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';

const { Switch } = LegacyForms;

interface Props {
dashboard: DashboardModel;
panel: PanelModel;
data: DataFrame[];
isLoading: boolean;
options: GetDataOptions;
onOptionsChange: (options: GetDataOptions) => void;
}

interface State {
Expand Down Expand Up @@ -55,6 +64,10 @@ export class InspectDataTab extends PureComponent<Props, State> {

onTransformationChange = (value: SelectableValue<DataTransformerID>) => {
this.setState({ transformId: value.value, dataFrameIndex: 0 });
this.props.onOptionsChange({
...this.props.options,
withTransforms: false,
});
};

getTransformedData(): DataFrame[] {
Expand All @@ -74,18 +87,27 @@ export class InspectDataTab extends PureComponent<Props, State> {
}

getProcessedData(): DataFrame[] {
const { options } = this.props;
let data = this.props.data;

if (this.state.transformId !== DataTransformerID.noop) {
data = this.getTransformedData();
}

// We need to apply field config even though it was already applied in the PanelQueryRunner.
// That's because transformers create new fields and data frames, so i.e. display processor is no longer there
return applyFieldOverrides({
data: this.getTransformedData(),
data,
theme: config.theme,
fieldConfig: { defaults: {}, overrides: [] },
fieldConfig: options.withFieldConfig ? this.props.panel.fieldConfig : { defaults: {}, overrides: [] },
replaceVariables: (value: string) => {
return value;
},
});
}

render() {
const { isLoading, data } = this.props;
const { isLoading, data, options, onOptionsChange } = this.props;
const { dataFrameIndex, transformId, transformationOptions } = this.state;
const styles = getPanelInspectorStyles();

Expand All @@ -110,25 +132,73 @@ export class InspectDataTab extends PureComponent<Props, State> {
};
});

const panelTransformations = this.props.panel.getTransformations();

return (
<div className={styles.dataTabContent} aria-label={selectors.components.PanelInspector.Data.content}>
<div className={styles.toolbar}>
{data.length > 1 && (
<Field label="Transform data" className="flex-grow-1">
<Select options={transformationOptions} value={transformId} onChange={this.onTransformationChange} />
</Field>
)}
{choices.length > 1 && (
<Field label="Select result" className={cx(styles.toolbarItem, 'flex-grow-1')}>
<Select options={choices} value={dataFrameIndex} onChange={this.onSelectedFrameChanged} />
</Field>
)}
<div className={styles.downloadCsv}>
<div className={styles.actionsWrapper}>
<div className={styles.leftActions}>
<div className={styles.selects}>
{data.length > 1 && (
<Field
label="Transformer"
className={css`
margin-bottom: 0;
`}
>
<Select
options={transformationOptions}
value={transformId}
onChange={this.onTransformationChange}
width={15}
/>
</Field>
)}
{choices.length > 1 && (
<Field
label="Select result"
className={css`
margin-bottom: 0;
`}
>
<Select options={choices} value={dataFrameIndex} onChange={this.onSelectedFrameChanged} />
</Field>
)}
</div>

<div className={cx(styles.options, styles.dataDisplayOptions)}>
<QueryOperationRow title={'Data display options'} isOpen={false}>
{panelTransformations && panelTransformations.length > 0 && (transformId as any) !== 'join by time' && (
<div className="gf-form-inline">
<Switch
tooltip="Data shown in the table will be transformed using transformations defined in the panel"
label="Apply panel transformations"
labelClass="width-12"
checked={!!options.withTransforms}
onChange={() => onOptionsChange({ ...options, withTransforms: !options.withTransforms })}
/>
</div>
)}
<div className="gf-form-inline">
<Switch
tooltip="Data shown in the table will have panel field configuration applied, for example units or display name"
label="Apply field configuration"
labelClass="width-12"
checked={!!options.withFieldConfig}
onChange={() => onOptionsChange({ ...options, withFieldConfig: !options.withFieldConfig })}
/>
</div>
</QueryOperationRow>
</div>
</div>

<div className={styles.options}>
<Button variant="primary" onClick={() => this.exportCsv(dataFrames[dataFrameIndex])}>
Download CSV
</Button>
</div>
</div>

<div style={{ flexGrow: 1 }}>
<AutoSizer>
{({ width, height }) => {
Expand Down
Expand Up @@ -28,6 +28,7 @@ import { getPanelInspectorStyles } from './styles';
import { StoreState } from 'app/types';
import { InspectDataTab } from './InspectDataTab';
import { supportsDataQuery } from '../PanelEditor/utils';
import { GetDataOptions } from '../../state/PanelQueryRunner';

interface OwnProps {
dashboard: DashboardModel;
Expand Down Expand Up @@ -62,6 +63,8 @@ interface State {
metaDS?: DataSourceApi;
// drawer width
drawerWidth: string;
withTransforms: boolean;
withFieldConfig: boolean;
}

export class PanelInspectorUnconnected extends PureComponent<Props, State> {
Expand All @@ -76,6 +79,8 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
data: [],
currentTab: props.defaultTab ?? InspectTab.Data,
drawerWidth: '50%',
withTransforms: true,
withFieldConfig: false,
};
}

Expand All @@ -87,8 +92,12 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
}
}

componentDidUpdate(prevProps: Props) {
if (prevProps.plugin !== this.props.plugin) {
componentDidUpdate(prevProps: Props, prevState: State) {
if (
prevProps.plugin !== this.props.plugin ||
this.state.withTransforms !== prevState.withTransforms ||
this.state.withFieldConfig !== prevState.withFieldConfig
) {
this.init();
}
}
Expand All @@ -99,11 +108,15 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
*/
init() {
const { plugin, panel } = this.props;
const { withTransforms, withFieldConfig } = this.state;

if (plugin && !plugin.meta.skipDataQuery) {
if (this.querySubscription) {
this.querySubscription.unsubscribe();
}
this.querySubscription = panel
.getQueryRunner()
.getData()
.getData({ withTransforms, withFieldConfig })
.subscribe({
next: data => this.onUpdateData(data),
});
Expand Down Expand Up @@ -164,6 +177,9 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
onSelectTab = (item: SelectableValue<InspectTab>) => {
this.setState({ currentTab: item.value || InspectTab.Data });
};
onDataTabOptionsChange = (options: GetDataOptions) => {
this.setState({ withTransforms: !!options.withTransforms, withFieldConfig: !!options.withFieldConfig });
};

renderMetadataInspector() {
const { metaDS, data } = this.state;
Expand All @@ -174,8 +190,20 @@ export class PanelInspectorUnconnected extends PureComponent<Props, State> {
}

renderDataTab() {
const { last, isLoading } = this.state;
return <InspectDataTab data={last.series} isLoading={isLoading} />;
const { last, isLoading, withFieldConfig, withTransforms } = this.state;
return (
<InspectDataTab
dashboard={this.props.dashboard}
panel={this.props.panel}
data={last.series}
isLoading={isLoading}
options={{
withFieldConfig,
withTransforms,
}}
onOptionsChange={this.onDataTabOptionsChange}
/>
);
}

renderErrorTab(error?: DataQueryError) {
Expand Down
25 changes: 22 additions & 3 deletions public/app/features/dashboard/components/Inspector/styles.ts
Expand Up @@ -41,9 +41,6 @@ export const getPanelInspectorStyles = stylesFactory(() => {
dataFrameSelect: css`
flex-grow: 2;
`,
downloadCsv: css`
margin-left: 16px;
`,
tabContent: css`
height: 100%;
display: flex;
Expand All @@ -55,5 +52,27 @@ export const getPanelInspectorStyles = stylesFactory(() => {
height: 100%;
width: 100%;
`,
actionsWrapper: css`
display: flex;
flex-wrap: wrap;
`,
leftActions: css`
display: flex;
flex-grow: 1;
`,
options: css`
margin-top: 19px;
`,
dataDisplayOptions: css`
flex-grow: 1;
min-width: 300px;
margin-right: ${config.theme.spacing.sm};
`,
selects: css`
display: flex;
> * {
margin-right: ${config.theme.spacing.sm};
}
`,
};
});
Expand Up @@ -17,7 +17,7 @@ export function initPanelEditor(sourcePanel: PanelModel, dashboard: DashboardMod
const panel = dashboard.initEditPanel(sourcePanel);

const queryRunner = panel.getQueryRunner();
const querySubscription = queryRunner.getData(false).subscribe({
const querySubscription = queryRunner.getData({ withTransforms: false }).subscribe({
next: (data: PanelData) => dispatch(setEditorPanelData(data)),
});

Expand Down
Expand Up @@ -78,7 +78,7 @@ export class PanelChromeAngularUnconnected extends PureComponent<Props, State> {

// subscribe to data events
const queryRunner = panel.getQueryRunner();
this.querySubscription = queryRunner.getData(false).subscribe({
this.querySubscription = queryRunner.getData({ withTransforms: false }).subscribe({
next: (data: PanelData) => this.onPanelDataUpdate(data),
});
}
Expand Down
4 changes: 2 additions & 2 deletions public/app/features/dashboard/panel_editor/QueriesTab.tsx
Expand Up @@ -50,7 +50,7 @@ interface State {
export class QueriesTab extends PureComponent<Props, State> {
datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
backendSrv = backendSrv;
querySubscription: Unsubscribable;
querySubscription: Unsubscribable | null;

state: State = {
isLoadingHelp: false,
Expand All @@ -71,7 +71,7 @@ export class QueriesTab extends PureComponent<Props, State> {
const { panel } = this.props;
const queryRunner = panel.getQueryRunner();

this.querySubscription = queryRunner.getData(false).subscribe({
this.querySubscription = queryRunner.getData({ withTransforms: false }).subscribe({
next: (data: PanelData) => this.onPanelDataUpdate(data),
});

Expand Down

0 comments on commit f23ecc4

Please sign in to comment.