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

216 lines
5.1 KiB
TypeScript
Raw Normal View History

// Library
2018-10-14 09:31:20 -05:00
import React, { Component } from 'react';
2019-01-09 03:33:20 -06:00
import { Tooltip } from '@grafana/ui';
import { Themes } from '@grafana/ui/src/components/Tooltip/Popper';
import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary';
// Services
2018-11-14 06:20:19 -06:00
import { getDatasourceSrv, DatasourceSrv } from 'app/features/plugins/datasource_srv';
// Utils
import kbn from 'app/core/utils/kbn';
// Types
import { DataQueryOptions, DataQueryResponse } from 'app/types';
import { TimeRange, TimeSeries, LoadingState } from '@grafana/ui';
2018-06-28 06:31:55 -05:00
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
interface RenderProps {
loading: LoadingState;
2018-10-14 11:19:49 -05:00
timeSeries: TimeSeries[];
2018-06-28 06:31:55 -05:00
}
export interface Props {
datasource: string | null;
queries: any[];
panelId?: number;
dashboardId?: number;
isVisible?: boolean;
timeRange?: TimeRange;
2018-11-13 11:50:12 -06:00
widthPixels: number;
refreshCounter: number;
minInterval?: string;
maxDataPoints?: number;
children: (r: RenderProps) => JSX.Element;
2018-06-28 06:31:55 -05:00
}
export interface State {
isFirstLoad: boolean;
loading: LoadingState;
errorMessage: string;
2018-10-14 11:19:49 -05:00
response: DataQueryResponse;
2018-06-28 06:31:55 -05:00
}
2018-10-14 09:31:20 -05:00
export class DataPanel extends Component<Props, State> {
static defaultProps = {
isVisible: true,
panelId: 1,
dashboardId: 1,
};
2018-07-01 10:34:42 -05:00
2018-11-14 06:20:19 -06:00
dataSourceSrv: DatasourceSrv = getDatasourceSrv();
isUnmounted = false;
constructor(props: Props) {
super(props);
2018-07-01 10:34:42 -05:00
this.state = {
loading: LoadingState.NotStarted,
errorMessage: '',
2018-10-14 11:19:49 -05:00
response: {
data: [],
},
isFirstLoad: true,
};
}
2018-07-01 10:34:42 -05:00
componentDidMount() {
this.issueQueries();
2018-10-14 09:31:20 -05:00
}
2018-11-14 06:20:19 -06:00
componentWillUnmount() {
this.isUnmounted = true;
}
2018-10-14 09:31:20 -05:00
async componentDidUpdate(prevProps: Props) {
if (!this.hasPropsChanged(prevProps)) {
return;
}
this.issueQueries();
}
2018-07-01 10:34:42 -05:00
2018-10-14 09:31:20 -05:00
hasPropsChanged(prevProps: Props) {
return this.props.refreshCounter !== prevProps.refreshCounter;
2018-10-14 09:31:20 -05:00
}
2018-11-14 06:20:19 -06:00
private issueQueries = async () => {
const { isVisible, queries, datasource, panelId, dashboardId, timeRange, widthPixels, maxDataPoints } = this.props;
if (!isVisible) {
return;
}
if (!queries.length) {
2018-10-14 11:19:49 -05:00
this.setState({ loading: LoadingState.Done });
return;
}
this.setState({ loading: LoadingState.Loading, errorMessage: '' });
try {
2018-11-13 11:50:12 -06: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 14:52:24 -05: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,
scopedVars: {},
cacheTimeout: null,
};
2018-10-14 11:19:49 -05:00
console.log('Issuing DataPanel query', queryOptions);
const resp = await ds.query(queryOptions);
2018-10-14 11:19:49 -05:00
console.log('Issuing DataPanel query Resp', resp);
2018-11-14 06:20:19 -06:00
if (this.isUnmounted) {
return;
}
2018-10-14 11:19:49 -05:00
this.setState({
loading: LoadingState.Done,
response: resp,
2018-10-22 02:53:40 -05:00
isFirstLoad: false,
2018-10-14 11:19:49 -05:00
});
} catch (err) {
console.log('Loading error', err);
this.onError('Request Error');
}
};
onError = (errorMessage: string) => {
if (this.state.loading !== LoadingState.Error || this.state.errorMessage !== errorMessage) {
this.setState({
loading: LoadingState.Error,
isFirstLoad: false,
2019-01-09 03:33:20 -06:00
errorMessage: errorMessage,
});
}
2019-01-09 03:33:20 -06:00
};
render() {
const { queries } = this.props;
2018-10-14 11:19:49 -05:00
const { response, loading, isFirstLoad } = this.state;
2018-11-14 04:06:02 -06:00
2018-10-14 11:19:49 -05:00
const timeSeries = response.data;
2018-11-14 06:20:19 -06:00
if (isFirstLoad && loading === LoadingState.Loading) {
2019-01-07 06:05:27 -06:00
return this.renderLoadingStates();
2018-11-14 06:20:19 -06:00
}
2018-07-01 10:34:42 -05:00
if (!queries.length) {
return (
<div className="panel-empty">
<p>Add a query to get some data!</p>
</div>
);
}
2018-07-01 10:34:42 -05:00
return (
<>
2019-01-07 06:05:27 -06:00
{this.renderLoadingStates()}
<ErrorBoundary>
2019-01-09 03:33:20 -06:00
{({ error, errorInfo }) => {
if (errorInfo) {
this.onError(error.message || DEFAULT_PLUGIN_ERROR);
return null;
}
return (
<>
{this.props.children({
timeSeries,
loading,
})}
</>
);
}}
</ErrorBoundary>
</>
);
}
2019-01-07 06:05:27 -06:00
private renderLoadingStates(): JSX.Element {
const { loading, errorMessage } = this.state;
if (loading === LoadingState.Loading) {
return (
<div className="panel-loading">
<i className="fa fa-spinner fa-spin" />
</div>
);
2018-12-19 05:58:10 -06:00
} else if (loading === LoadingState.Error) {
return (
2019-01-09 03:33:20 -06:00
<Tooltip content={errorMessage} placement="bottom-start" theme={Themes.Error}>
<div className="panel-info-corner panel-info-corner--error">
<i className="fa" />
<span className="panel-info-corner-inner" />
</div>
2018-12-19 05:58:10 -06:00
</Tooltip>
);
2018-07-01 10:34:42 -05:00
}
2018-06-28 06:31:55 -05:00
return null;
}
}