DataPanel.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Library
  2. import React, { Component } from 'react';
  3. // Services
  4. import { DatasourceSrv, getDatasourceSrv } from 'app/features/plugins/datasource_srv';
  5. // Utils
  6. import kbn from 'app/core/utils/kbn';
  7. // Types
  8. import {
  9. DataQueryOptions,
  10. DataQueryResponse,
  11. DataQueryError,
  12. LoadingState,
  13. PanelData,
  14. TableData,
  15. TimeRange,
  16. TimeSeries,
  17. ScopedVars,
  18. } from '@grafana/ui';
  19. interface RenderProps {
  20. loading: LoadingState;
  21. panelData: PanelData;
  22. }
  23. export interface Props {
  24. datasource: string | null;
  25. queries: any[];
  26. panelId: number;
  27. dashboardId?: number;
  28. isVisible?: boolean;
  29. timeRange?: TimeRange;
  30. widthPixels: number;
  31. refreshCounter: number;
  32. minInterval?: string;
  33. maxDataPoints?: number;
  34. scopedVars?: ScopedVars;
  35. children: (r: RenderProps) => JSX.Element;
  36. onDataResponse?: (data: DataQueryResponse) => void;
  37. onError: (message: string, error: DataQueryError) => void;
  38. }
  39. export interface State {
  40. isFirstLoad: boolean;
  41. loading: LoadingState;
  42. response: DataQueryResponse;
  43. panelData: PanelData;
  44. }
  45. export class DataPanel extends Component<Props, State> {
  46. static defaultProps = {
  47. isVisible: true,
  48. dashboardId: 1,
  49. };
  50. dataSourceSrv: DatasourceSrv = getDatasourceSrv();
  51. isUnmounted = false;
  52. constructor(props: Props) {
  53. super(props);
  54. this.state = {
  55. loading: LoadingState.NotStarted,
  56. response: {
  57. data: [],
  58. },
  59. panelData: {},
  60. isFirstLoad: true,
  61. };
  62. }
  63. componentDidMount() {
  64. this.issueQueries();
  65. }
  66. componentWillUnmount() {
  67. this.isUnmounted = true;
  68. }
  69. async componentDidUpdate(prevProps: Props) {
  70. if (!this.hasPropsChanged(prevProps)) {
  71. return;
  72. }
  73. this.issueQueries();
  74. }
  75. hasPropsChanged(prevProps: Props) {
  76. return this.props.refreshCounter !== prevProps.refreshCounter;
  77. }
  78. private issueQueries = async () => {
  79. const {
  80. isVisible,
  81. queries,
  82. datasource,
  83. panelId,
  84. dashboardId,
  85. timeRange,
  86. widthPixels,
  87. maxDataPoints,
  88. scopedVars,
  89. onDataResponse,
  90. onError,
  91. } = this.props;
  92. if (!isVisible) {
  93. return;
  94. }
  95. if (!queries.length) {
  96. this.setState({ loading: LoadingState.Done });
  97. return;
  98. }
  99. this.setState({ loading: LoadingState.Loading });
  100. try {
  101. const ds = await this.dataSourceSrv.get(datasource);
  102. // TODO interpolate variables
  103. const minInterval = this.props.minInterval || ds.interval;
  104. const intervalRes = kbn.calculateInterval(timeRange, widthPixels, minInterval);
  105. const queryOptions: DataQueryOptions = {
  106. timezone: 'browser',
  107. panelId: panelId,
  108. dashboardId: dashboardId,
  109. range: timeRange,
  110. rangeRaw: timeRange.raw,
  111. interval: intervalRes.interval,
  112. intervalMs: intervalRes.intervalMs,
  113. targets: queries,
  114. maxDataPoints: maxDataPoints || widthPixels,
  115. scopedVars: scopedVars || {},
  116. cacheTimeout: null,
  117. };
  118. const resp = await ds.query(queryOptions);
  119. if (this.isUnmounted) {
  120. return;
  121. }
  122. if (onDataResponse) {
  123. onDataResponse(resp);
  124. }
  125. this.setState({
  126. loading: LoadingState.Done,
  127. response: resp,
  128. panelData: this.getPanelData(resp),
  129. isFirstLoad: false,
  130. });
  131. } catch (err) {
  132. console.log('DataPanel error', err);
  133. let message = 'Query error';
  134. if (err.message) {
  135. message = err.message;
  136. } else if (err.data && err.data.message) {
  137. message = err.data.message;
  138. } else if (err.data && err.data.error) {
  139. message = err.data.error;
  140. } else if (err.status) {
  141. message = `Query error: ${err.status} ${err.statusText}`;
  142. }
  143. onError(message, err);
  144. this.setState({ isFirstLoad: false, loading: LoadingState.Error });
  145. }
  146. };
  147. getPanelData(response: DataQueryResponse) {
  148. if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') {
  149. return {
  150. tableData: response.data[0] as TableData,
  151. timeSeries: null,
  152. };
  153. }
  154. return {
  155. timeSeries: response.data as TimeSeries[],
  156. tableData: null,
  157. };
  158. }
  159. render() {
  160. const { queries } = this.props;
  161. const { loading, isFirstLoad, panelData } = this.state;
  162. // do not render component until we have first data
  163. if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
  164. return this.renderLoadingState();
  165. }
  166. if (!queries.length) {
  167. return (
  168. <div className="panel-empty">
  169. <p>Add a query to get some data!</p>
  170. </div>
  171. );
  172. }
  173. return (
  174. <>
  175. {loading === LoadingState.Loading && this.renderLoadingState()}
  176. {this.props.children({ loading, panelData })}
  177. </>
  178. );
  179. }
  180. private renderLoadingState(): JSX.Element {
  181. return (
  182. <div className="panel-loading">
  183. <i className="fa fa-spinner fa-spin" />
  184. </div>
  185. );
  186. }
  187. }