mirror of
https://github.com/grafana/grafana.git
synced 2025-01-06 14:13:11 -06:00
Refactor: React Panels to only use SeriesData[] (#16306)
* only use SeriesData[] in react panels * update target * Refactor: Added refId filtering for queryResponse and queryError
This commit is contained in:
parent
6e54a7ae05
commit
ede2d54849
@ -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 {
|
||||
|
@ -1,6 +1,14 @@
|
||||
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,19 +54,36 @@ 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) {
|
||||
// for some reason this needs to be done in next tick
|
||||
@ -70,11 +91,12 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
onPanelDataReceived = () => {
|
||||
// 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);
|
||||
// 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 });
|
||||
}
|
||||
};
|
||||
|
||||
@ -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>;
|
||||
|
Loading…
Reference in New Issue
Block a user