|
|
@@ -3,15 +3,7 @@ import { hot } from 'react-hot-loader';
|
|
|
import Select from 'react-select';
|
|
|
import _ from 'lodash';
|
|
|
|
|
|
-import {
|
|
|
- ExploreState,
|
|
|
- ExploreUrlState,
|
|
|
- HistoryItem,
|
|
|
- Query,
|
|
|
- QueryTransaction,
|
|
|
- Range,
|
|
|
- ResultType,
|
|
|
-} from 'app/types/explore';
|
|
|
+import { ExploreState, ExploreUrlState, HistoryItem, Query, QueryTransaction, ResultType } from 'app/types/explore';
|
|
|
import kbn from 'app/core/utils/kbn';
|
|
|
import colors from 'app/core/utils/colors';
|
|
|
import store from 'app/core/store';
|
|
|
@@ -24,12 +16,14 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
|
|
|
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
|
|
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
|
|
|
|
|
+import ErrorBoundary from './ErrorBoundary';
|
|
|
import QueryRows from './QueryRows';
|
|
|
import Graph from './Graph';
|
|
|
import Logs from './Logs';
|
|
|
import Table from './Table';
|
|
|
import TimePicker from './TimePicker';
|
|
|
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
|
|
+import { RawTimeRange } from 'app/types/series';
|
|
|
|
|
|
const MAX_HISTORY_ITEMS = 100;
|
|
|
|
|
|
@@ -154,11 +148,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- componentDidCatch(error) {
|
|
|
- this.setState({ datasourceError: error });
|
|
|
- console.error(error);
|
|
|
- }
|
|
|
-
|
|
|
async setDatasource(datasource) {
|
|
|
const supportsGraph = datasource.meta.metrics;
|
|
|
const supportsLogs = datasource.meta.logs;
|
|
|
@@ -170,7 +159,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
const testResult = await datasource.testDatasource();
|
|
|
datasourceError = testResult.status === 'success' ? null : testResult.message;
|
|
|
} catch (error) {
|
|
|
- datasourceError = (error && error.statusText) || error;
|
|
|
+ datasourceError = (error && error.statusText) || 'Network error';
|
|
|
}
|
|
|
|
|
|
const historyKey = `grafana.explore.history.${datasourceId}`;
|
|
|
@@ -278,10 +267,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- onChangeTime = nextRange => {
|
|
|
- const range = {
|
|
|
- from: nextRange.from,
|
|
|
- to: nextRange.to,
|
|
|
+ onChangeTime = (nextRange: RawTimeRange) => {
|
|
|
+ const range: RawTimeRange = {
|
|
|
+ ...nextRange,
|
|
|
};
|
|
|
this.setState({ range }, () => this.onSubmit());
|
|
|
};
|
|
|
@@ -471,7 +459,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
) {
|
|
|
const { datasource, range } = this.state;
|
|
|
const resolution = this.el.offsetWidth;
|
|
|
- const absoluteRange = {
|
|
|
+ const absoluteRange: RawTimeRange = {
|
|
|
from: parseDate(range.from, false),
|
|
|
to: parseDate(range.to, true),
|
|
|
};
|
|
|
@@ -486,7 +474,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
];
|
|
|
|
|
|
// Clone range for query request
|
|
|
- const queryRange: Range = { ...range };
|
|
|
+ const queryRange: RawTimeRange = { ...range };
|
|
|
|
|
|
return {
|
|
|
interval,
|
|
|
@@ -584,13 +572,28 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- failQueryTransaction(transactionId: string, error: string, datasourceId: string) {
|
|
|
+ failQueryTransaction(transactionId: string, response: any, datasourceId: string) {
|
|
|
const { datasource } = this.state;
|
|
|
if (datasource.meta.id !== datasourceId) {
|
|
|
// Navigated away, queries did not matter
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ console.error(response);
|
|
|
+
|
|
|
+ let error: string | JSX.Element = response;
|
|
|
+ if (response.data) {
|
|
|
+ error = response.data.error;
|
|
|
+ if (response.data.response) {
|
|
|
+ error = (
|
|
|
+ <>
|
|
|
+ <span>{response.data.error}</span>
|
|
|
+ <details>{response.data.response}</details>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
this.setState(state => {
|
|
|
// Transaction might have been discarded
|
|
|
if (!state.queryTransactions.find(qt => qt.id === transactionId)) {
|
|
|
@@ -637,9 +640,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
|
|
|
this.setState({ graphRange: transaction.options.range });
|
|
|
} catch (response) {
|
|
|
- console.error(response);
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
- this.failQueryTransaction(transaction.id, queryError, datasourceId);
|
|
|
+ this.failQueryTransaction(transaction.id, response, datasourceId);
|
|
|
}
|
|
|
} else {
|
|
|
this.discardTransactions(rowIndex);
|
|
|
@@ -669,9 +670,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
const results = res.data[0];
|
|
|
this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
|
|
|
} catch (response) {
|
|
|
- console.error(response);
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
- this.failQueryTransaction(transaction.id, queryError, datasourceId);
|
|
|
+ this.failQueryTransaction(transaction.id, response, datasourceId);
|
|
|
}
|
|
|
} else {
|
|
|
this.discardTransactions(rowIndex);
|
|
|
@@ -697,9 +696,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
const results = res.data;
|
|
|
this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
|
|
|
} catch (response) {
|
|
|
- console.error(response);
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
- this.failQueryTransaction(transaction.id, queryError, datasourceId);
|
|
|
+ this.failQueryTransaction(transaction.id, response, datasourceId);
|
|
|
}
|
|
|
} else {
|
|
|
this.discardTransactions(rowIndex);
|
|
|
@@ -751,15 +748,16 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
const graphLoading = queryTransactions.some(qt => qt.resultType === 'Graph' && !qt.done);
|
|
|
const tableLoading = queryTransactions.some(qt => qt.resultType === 'Table' && !qt.done);
|
|
|
const logsLoading = queryTransactions.some(qt => qt.resultType === 'Logs' && !qt.done);
|
|
|
+ // TODO don't recreate those on each re-render
|
|
|
const graphResult = _.flatten(
|
|
|
queryTransactions.filter(qt => qt.resultType === 'Graph' && qt.done && qt.result).map(qt => qt.result)
|
|
|
);
|
|
|
const tableResult = mergeTablesIntoModel(
|
|
|
new TableModel(),
|
|
|
- ...queryTransactions.filter(qt => qt.resultType === 'Table' && qt.done).map(qt => qt.result)
|
|
|
+ ...queryTransactions.filter(qt => qt.resultType === 'Table' && qt.done && qt.result).map(qt => qt.result)
|
|
|
);
|
|
|
const logsResult = _.flatten(
|
|
|
- queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done).map(qt => qt.result)
|
|
|
+ queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done && qt.result).map(qt => qt.result)
|
|
|
);
|
|
|
const loading = queryTransactions.some(qt => !qt.done);
|
|
|
|
|
|
@@ -868,23 +866,25 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
</div>
|
|
|
|
|
|
<main className="m-t-2">
|
|
|
- {supportsGraph &&
|
|
|
- showingGraph && (
|
|
|
- <Graph
|
|
|
- data={graphResult}
|
|
|
- height={graphHeight}
|
|
|
- loading={graphLoading}
|
|
|
- id={`explore-graph-${position}`}
|
|
|
- range={graphRange}
|
|
|
- split={split}
|
|
|
- />
|
|
|
- )}
|
|
|
- {supportsTable && showingTable ? (
|
|
|
- <div className="panel-container m-t-2">
|
|
|
- <Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
|
|
|
- </div>
|
|
|
- ) : null}
|
|
|
- {supportsLogs && showingLogs ? <Logs data={logsResult} loading={logsLoading} /> : null}
|
|
|
+ <ErrorBoundary>
|
|
|
+ {supportsGraph &&
|
|
|
+ showingGraph && (
|
|
|
+ <Graph
|
|
|
+ data={graphResult}
|
|
|
+ height={graphHeight}
|
|
|
+ loading={graphLoading}
|
|
|
+ id={`explore-graph-${position}`}
|
|
|
+ range={graphRange}
|
|
|
+ split={split}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {supportsTable && showingTable ? (
|
|
|
+ <div className="panel-container m-t-2">
|
|
|
+ <Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
|
|
|
+ </div>
|
|
|
+ ) : null}
|
|
|
+ {supportsLogs && showingLogs ? <Logs data={logsResult} loading={logsLoading} /> : null}
|
|
|
+ </ErrorBoundary>
|
|
|
</main>
|
|
|
</div>
|
|
|
) : null}
|