Files
grafana/public/app/features/dashboard/dashgrid/DataPanel.tsx

222 lines
4.9 KiB
TypeScript
Raw Normal View History

// Library
2018-10-14 16:31:20 +02:00
import React, { Component } from 'react';
2019-01-09 10:33:20 +01:00
// Services
2019-01-30 10:39:42 +01:00
import { DatasourceSrv, getDatasourceSrv } from 'app/features/plugins/datasource_srv';
// Utils
import kbn from 'app/core/utils/kbn';
// Types
2019-01-31 09:59:21 +01:00
import {
DataQueryOptions,
DataQueryResponse,
DataQueryError,
LoadingState,
2019-01-31 09:59:21 +01:00
PanelData,
TableData,
TimeRange,
TimeSeries,
2019-03-04 10:42:59 +01:00
ScopedVars,
} from '@grafana/ui';
2018-06-28 04:31:55 -07:00
interface RenderProps {
loading: LoadingState;
2019-01-31 09:59:21 +01:00
panelData: PanelData;
2018-06-28 04:31:55 -07:00
}
export interface Props {
datasource: string | null;
queries: any[];
panelId: number;
dashboardId?: number;
isVisible?: boolean;
timeRange?: TimeRange;
2018-11-13 18:50:12 +01:00
widthPixels: number;
refreshCounter: number;
minInterval?: string;
maxDataPoints?: number;
2019-03-04 10:42:59 +01:00
scopedVars?: ScopedVars;
children: (r: RenderProps) => JSX.Element;
2019-01-30 10:39:42 +01:00
onDataResponse?: (data: DataQueryResponse) => void;
onError: (message: string, error: DataQueryError) => void;
2018-06-28 04:31:55 -07:00
}
export interface State {
isFirstLoad: boolean;
loading: LoadingState;
2018-10-14 18:19:49 +02:00
response: DataQueryResponse;
2018-06-28 04:31:55 -07:00
}
2018-10-14 16:31:20 +02:00
export class DataPanel extends Component<Props, State> {
static defaultProps = {
isVisible: true,
dashboardId: 1,
};
2018-07-01 17:34:42 +02:00
2018-11-14 13:20:19 +01:00
dataSourceSrv: DatasourceSrv = getDatasourceSrv();
isUnmounted = false;
constructor(props: Props) {
super(props);
2018-07-01 17:34:42 +02:00
this.state = {
loading: LoadingState.NotStarted,
2018-10-14 18:19:49 +02:00
response: {
data: [],
},
isFirstLoad: true,
};
}
2018-07-01 17:34:42 +02:00
componentDidMount() {
this.issueQueries();
2018-10-14 16:31:20 +02:00
}
2018-11-14 13:20:19 +01:00
componentWillUnmount() {
this.isUnmounted = true;
}
2018-10-14 16:31:20 +02:00
async componentDidUpdate(prevProps: Props) {
if (!this.hasPropsChanged(prevProps)) {
return;
}
this.issueQueries();
}
2018-07-01 17:34:42 +02:00
2018-10-14 16:31:20 +02:00
hasPropsChanged(prevProps: Props) {
return this.props.refreshCounter !== prevProps.refreshCounter;
2018-10-14 16:31:20 +02:00
}
2018-11-14 13:20:19 +01:00
private issueQueries = async () => {
2019-01-30 10:39:42 +01:00
const {
isVisible,
queries,
datasource,
panelId,
dashboardId,
timeRange,
widthPixels,
maxDataPoints,
2019-03-04 10:42:59 +01:00
scopedVars,
2019-01-30 10:39:42 +01:00
onDataResponse,
onError,
2019-01-30 10:39:42 +01:00
} = this.props;
if (!isVisible) {
return;
}
if (!queries.length) {
2018-10-14 18:19:49 +02:00
this.setState({ loading: LoadingState.Done });
return;
}
this.setState({ loading: LoadingState.Loading });
try {
2018-11-13 18:50:12 +01:00
const ds = await this.dataSourceSrv.get(datasource);
// TODO interpolate variables
const minInterval = this.props.minInterval || ds.interval;
const intervalRes = kbn.calculateInterval(timeRange, widthPixels, minInterval);
2018-10-15 21:52:24 +02:00
const queryOptions: DataQueryOptions = {
timezone: 'browser',
panelId: panelId,
dashboardId: dashboardId,
range: timeRange,
rangeRaw: timeRange.raw,
interval: intervalRes.interval,
intervalMs: intervalRes.intervalMs,
targets: queries,
maxDataPoints: maxDataPoints || widthPixels,
2019-03-04 10:42:59 +01:00
scopedVars: scopedVars || {},
cacheTimeout: null,
};
const resp = await ds.query(queryOptions);
2019-01-31 09:59:21 +01:00
2018-11-14 13:20:19 +01:00
if (this.isUnmounted) {
return;
}
2019-01-30 10:39:42 +01:00
if (onDataResponse) {
onDataResponse(resp);
}
2018-10-14 18:19:49 +02:00
this.setState({
loading: LoadingState.Done,
response: resp,
2018-10-22 09:53:40 +02:00
isFirstLoad: false,
2018-10-14 18:19:49 +02:00
});
} catch (err) {
console.log('DataPanel error', err);
let message = 'Query error';
if (err.message) {
message = err.message;
} else if (err.data && err.data.message) {
message = err.data.message;
} else if (err.data && err.data.error) {
message = err.data.error;
2019-02-14 15:23:34 +01:00
} else if (err.status) {
message = `Query error: ${err.status} ${err.statusText}`;
}
onError(message, err);
this.setState({ isFirstLoad: false, loading: LoadingState.Error });
}
};
2019-01-31 09:59:21 +01:00
getPanelData = () => {
const { response } = this.state;
if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') {
return {
2019-01-31 13:59:25 +01:00
tableData: response.data[0] as TableData,
timeSeries: null,
2019-01-31 09:59:21 +01:00
};
}
return {
2019-01-31 13:59:25 +01:00
timeSeries: response.data as TimeSeries[],
tableData: null,
2019-01-31 09:59:21 +01:00
};
};
render() {
const { queries } = this.props;
2019-01-31 09:59:21 +01:00
const { loading, isFirstLoad } = this.state;
const panelData = this.getPanelData();
// do not render component until we have first data
if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
2019-02-14 15:29:36 +01:00
return this.renderLoadingState();
}
2018-07-01 17:34:42 +02:00
if (!queries.length) {
return (
<div className="panel-empty">
<p>Add a query to get some data!</p>
</div>
);
2018-07-01 17:34:42 +02:00
}
2018-06-28 04:31:55 -07:00
2019-02-14 15:29:36 +01:00
return (
<>
{loading === LoadingState.Loading && this.renderLoadingState()}
2019-02-14 15:29:36 +01:00
{this.props.children({ loading, panelData })}
</>
);
}
private renderLoadingState(): JSX.Element {
return (
<div className="panel-loading">
<i className="fa fa-spinner fa-spin" />
</div>
);
}
}