Commit ede2d548 by Ryan McKinley Committed by Torkel Ödegaard

Refactor: React Panels to only use SeriesData[] (#16306)

* only use SeriesData[] in react panels
* update target
* Refactor: Added refId filtering for queryResponse and queryError
parent 6e54a7ae
......@@ -13,6 +13,17 @@ export enum FieldType {
other = 'other', // Object, Array, etc
}
export interface QueryResultBase {
/**
* Matches the query target refId
*/
refId?: string;
/**
* Used by some backend datasources to communicate back info about the execution (generated sql, timing)
*/
meta?: any;
}
export interface Field {
name: string; // The column name
type?: FieldType;
......@@ -25,7 +36,7 @@ export interface Labels {
[key: string]: string;
}
export interface SeriesData {
export interface SeriesData extends QueryResultBase {
name?: string;
fields: Field[];
rows: any[][];
......@@ -38,7 +49,7 @@ export interface Column {
unit?: string;
}
export interface TableData {
export interface TableData extends QueryResultBase {
columns: Column[];
rows: any[][];
}
......@@ -47,7 +58,7 @@ export type TimeSeriesValue = number | null;
export type TimeSeriesPoints = TimeSeriesValue[][];
export interface TimeSeries {
export interface TimeSeries extends QueryResultBase {
target: string;
datapoints: TimeSeriesPoints;
unit?: string;
......
......@@ -44,6 +44,7 @@ export interface DataQueryError {
message?: string;
status?: string;
statusText?: string;
refId?: string;
}
export interface ScopedVar {
......
import { ComponentClass } from 'react';
import { ReactPanelPlugin } from './panel';
import { DataQueryOptions, DataQuery, DataQueryResponse, QueryHint, QueryFixAction } from './datasource';
import {
DataQueryOptions,
DataQuery,
DataQueryResponse,
QueryHint,
QueryFixAction,
DataQueryError,
} from './datasource';
import { SeriesData } from './data';
export interface DataSourceApi<TQuery extends DataQuery = DataQuery> {
/**
......@@ -52,6 +60,8 @@ export interface QueryEditorProps<DSType extends DataSourceApi, TQuery extends D
query: TQuery;
onRunQuery: () => void;
onChange: (value: TQuery) => void;
queryResponse?: SeriesData[];
queryError?: DataQueryError;
}
export enum DatasourceStatus {
......
......@@ -17,6 +17,8 @@ function convertTableToSeriesData(table: TableData): SeriesData {
return f;
}),
rows: table.rows,
refId: table.refId,
meta: table.meta,
};
}
......@@ -36,6 +38,8 @@ function convertTimeSeriesToSeriesData(timeSeries: TimeSeries): SeriesData {
],
rows: timeSeries.datapoints,
labels: timeSeries.tags,
refId: timeSeries.refId,
meta: timeSeries.meta,
};
}
......@@ -168,6 +172,8 @@ export const toLegacyResponseData = (series: SeriesData): TimeSeries | TableData
target: fields[0].name || series.name,
datapoints: rows,
unit: fields[0].unit,
refId: series.refId,
meta: series.meta,
} as TimeSeries;
}
}
......@@ -178,6 +184,8 @@ export const toLegacyResponseData = (series: SeriesData): TimeSeries | TableData
text: f.name,
filterable: f.filterable,
unit: f.unit,
refId: series.refId,
meta: series.meta,
};
}),
rows,
......
......@@ -128,10 +128,18 @@ export class DashboardPanel extends PureComponent<Props, State> {
};
renderReactPanel() {
const { dashboard, panel, isFullscreen } = this.props;
const { dashboard, panel, isFullscreen, isEditing } = this.props;
const { plugin } = this.state;
return <PanelChrome plugin={plugin} panel={panel} dashboard={dashboard} isFullscreen={isFullscreen} />;
return (
<PanelChrome
plugin={plugin}
panel={panel}
dashboard={dashboard}
isFullscreen={isFullscreen}
isEditing={isEditing}
/>
);
}
renderAngularPanel() {
......
......@@ -8,7 +8,6 @@ import kbn from 'app/core/utils/kbn';
// Types
import {
DataQueryOptions,
DataQueryResponse,
DataQueryError,
LoadingState,
SeriesData,
......@@ -36,14 +35,13 @@ export interface Props {
maxDataPoints?: number;
scopedVars?: ScopedVars;
children: (r: RenderProps) => JSX.Element;
onDataResponse?: (data: DataQueryResponse) => void;
onDataResponse?: (data?: SeriesData[]) => void;
onError: (message: string, error: DataQueryError) => void;
}
export interface State {
isFirstLoad: boolean;
loading: LoadingState;
response: DataQueryResponse;
data?: SeriesData[];
}
......@@ -80,9 +78,6 @@ export class DataPanel extends Component<Props, State> {
this.state = {
loading: LoadingState.NotStarted,
response: {
data: [],
},
isFirstLoad: true,
};
}
......@@ -160,14 +155,15 @@ export class DataPanel extends Component<Props, State> {
return;
}
// Make sure the data is SeriesData[]
const data = getProcessedSeriesData(resp.data);
if (onDataResponse) {
onDataResponse(resp);
onDataResponse(data);
}
this.setState({
data,
loading: LoadingState.Done,
response: resp,
data: getProcessedSeriesData(resp.data),
isFirstLoad: false,
});
} catch (err) {
......
......@@ -19,7 +19,7 @@ import config from 'app/core/config';
// Types
import { DashboardModel, PanelModel } from '../state';
import { PanelPlugin } from 'app/types';
import { DataQueryResponse, TimeRange, LoadingState, DataQueryError, SeriesData } from '@grafana/ui';
import { TimeRange, LoadingState, DataQueryError, SeriesData, toLegacyResponseData } from '@grafana/ui';
import { ScopedVars } from '@grafana/ui';
import templateSrv from 'app/features/templating/template_srv';
......@@ -33,6 +33,7 @@ export interface Props {
dashboard: DashboardModel;
plugin: PanelPlugin;
isFullscreen: boolean;
isEditing: boolean;
}
export interface State {
......@@ -96,15 +97,25 @@ export class PanelChrome extends PureComponent<Props, State> {
return templateSrv.replace(value, vars, format);
};
onDataResponse = (dataQueryResponse: DataQueryResponse) => {
onDataResponse = (data?: SeriesData[]) => {
if (this.props.dashboard.isSnapshot()) {
this.props.panel.snapshotData = dataQueryResponse.data;
this.props.panel.snapshotData = data;
}
// clear error state (if any)
this.clearErrorState();
// This event is used by old query editors and panel editor options
this.props.panel.events.emit('data-received', dataQueryResponse.data);
if (this.props.isEditing) {
const events = this.props.panel.events;
if (!data) {
data = [];
}
// Angular query editors expect TimeSeries|TableData
events.emit('data-received', data.map(v => toLegacyResponseData(v)));
// Notify react query editors
events.emit('series-data-received', data);
}
};
onDataError = (message: string, error: DataQueryError) => {
......
......@@ -11,7 +11,7 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
// Types
import { PanelModel } from '../state/PanelModel';
import { DataQuery, DataSourceApi, TimeRange } from '@grafana/ui';
import { DataQuery, DataSourceApi, TimeRange, DataQueryError, SeriesData } from '@grafana/ui';
import { DashboardModel } from '../state/DashboardModel';
interface Props {
......@@ -31,6 +31,8 @@ interface State {
datasource: DataSourceApi | null;
isCollapsed: boolean;
hasTextEditMode: boolean;
queryError: DataQueryError | null;
queryResponse: SeriesData[] | null;
}
export class QueryEditorRow extends PureComponent<Props, State> {
......@@ -43,6 +45,8 @@ export class QueryEditorRow extends PureComponent<Props, State> {
isCollapsed: false,
loadedDataSourceValue: undefined,
hasTextEditMode: false,
queryError: null,
queryResponse: null,
};
componentDidMount() {
......@@ -50,26 +54,35 @@ export class QueryEditorRow extends PureComponent<Props, State> {
this.props.panel.events.on('refresh', this.onPanelRefresh);
this.props.panel.events.on('data-error', this.onPanelDataError);
this.props.panel.events.on('data-received', this.onPanelDataReceived);
this.props.panel.events.on('series-data-received', this.onSeriesDataReceived);
}
componentWillUnmount() {
this.props.panel.events.off('refresh', this.onPanelRefresh);
this.props.panel.events.off('data-error', this.onPanelDataError);
this.props.panel.events.off('data-received', this.onPanelDataReceived);
this.props.panel.events.off('series-data-received', this.onSeriesDataReceived);
if (this.angularQueryEditor) {
this.angularQueryEditor.destroy();
}
}
onPanelDataError = () => {
onPanelDataError = (error: DataQueryError) => {
// Some query controllers listen to data error events and need a digest
if (this.angularQueryEditor) {
// for some reason this needs to be done in next tick
setTimeout(this.angularQueryEditor.digest);
return;
}
// if error relates to this query store it in state and pass it on to query editor
if (error.refId === this.props.query.refId) {
this.setState({ queryError: error });
}
};
// Only used by angular plugins
onPanelDataReceived = () => {
// Some query controllers listen to data error events and need a digest
if (this.angularQueryEditor) {
......@@ -78,6 +91,15 @@ export class QueryEditorRow extends PureComponent<Props, State> {
}
};
// Only used by the React Query Editors
onSeriesDataReceived = (data: SeriesData[]) => {
if (!this.angularQueryEditor) {
// only pass series related to this query to query editor
const filterByRefId = data.filter(series => series.refId === this.props.query.refId);
this.setState({ queryResponse: filterByRefId, queryError: null });
}
};
onPanelRefresh = () => {
if (this.angularScope) {
this.angularScope.range = getTimeSrv().timeRange();
......@@ -152,7 +174,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
renderPluginEditor() {
const { query, onChange } = this.props;
const { datasource } = this.state;
const { datasource, queryResponse, queryError } = this.state;
if (datasource.pluginExports.QueryCtrl) {
return <div ref={element => (this.element = element)} />;
......@@ -160,7 +182,16 @@ export class QueryEditorRow extends PureComponent<Props, State> {
if (datasource.pluginExports.QueryEditor) {
const QueryEditor = datasource.pluginExports.QueryEditor;
return <QueryEditor query={query} datasource={datasource} onChange={onChange} onRunQuery={this.onRunQuery} />;
return (
<QueryEditor
query={query}
datasource={datasource}
onChange={onChange}
onRunQuery={this.onRunQuery}
queryResponse={queryResponse}
queryError={queryError}
/>
);
}
return <div>Data source plugin does not export any Query Editor component</div>;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment