|
@@ -1,8 +1,17 @@
|
|
|
import React from 'react';
|
|
import React from 'react';
|
|
|
import { hot } from 'react-hot-loader';
|
|
import { hot } from 'react-hot-loader';
|
|
|
import Select from 'react-select';
|
|
import Select from 'react-select';
|
|
|
-
|
|
|
|
|
-import { ExploreState, ExploreUrlState, Query } from 'app/types/explore';
|
|
|
|
|
|
|
+import _ from 'lodash';
|
|
|
|
|
+
|
|
|
|
|
+import {
|
|
|
|
|
+ ExploreState,
|
|
|
|
|
+ ExploreUrlState,
|
|
|
|
|
+ HistoryItem,
|
|
|
|
|
+ Query,
|
|
|
|
|
+ QueryTransaction,
|
|
|
|
|
+ Range,
|
|
|
|
|
+ ResultType,
|
|
|
|
|
+} from 'app/types/explore';
|
|
|
import kbn from 'app/core/utils/kbn';
|
|
import kbn from 'app/core/utils/kbn';
|
|
|
import colors from 'app/core/utils/colors';
|
|
import colors from 'app/core/utils/colors';
|
|
|
import store from 'app/core/store';
|
|
import store from 'app/core/store';
|
|
@@ -13,8 +22,8 @@ import ResetStyles from 'app/core/components/Picker/ResetStyles';
|
|
|
import PickerOption from 'app/core/components/Picker/PickerOption';
|
|
import PickerOption from 'app/core/components/Picker/PickerOption';
|
|
|
import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
|
|
import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer';
|
|
|
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
|
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
|
|
|
|
+import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
|
|
|
|
|
|
|
-import ElapsedTime from './ElapsedTime';
|
|
|
|
|
import QueryRows from './QueryRows';
|
|
import QueryRows from './QueryRows';
|
|
|
import Graph from './Graph';
|
|
import Graph from './Graph';
|
|
|
import Logs from './Logs';
|
|
import Logs from './Logs';
|
|
@@ -24,16 +33,6 @@ import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
|
|
|
|
|
|
|
|
const MAX_HISTORY_ITEMS = 100;
|
|
const MAX_HISTORY_ITEMS = 100;
|
|
|
|
|
|
|
|
-function makeHints(hints) {
|
|
|
|
|
- const hintsByIndex = [];
|
|
|
|
|
- hints.forEach(hint => {
|
|
|
|
|
- if (hint) {
|
|
|
|
|
- hintsByIndex[hint.index] = hint;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- return hintsByIndex;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
function makeTimeSeriesList(dataList, options) {
|
|
function makeTimeSeriesList(dataList, options) {
|
|
|
return dataList.map((seriesData, index) => {
|
|
return dataList.map((seriesData, index) => {
|
|
|
const datapoints = seriesData.datapoints || [];
|
|
const datapoints = seriesData.datapoints || [];
|
|
@@ -52,6 +51,25 @@ function makeTimeSeriesList(dataList, options) {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * Update the query history. Side-effect: store history in local storage
|
|
|
|
|
+ */
|
|
|
|
|
+function updateHistory(history: HistoryItem[], datasourceId: string, queries: string[]): HistoryItem[] {
|
|
|
|
|
+ const ts = Date.now();
|
|
|
|
|
+ queries.forEach(query => {
|
|
|
|
|
+ history = [{ query, ts }, ...history];
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (history.length > MAX_HISTORY_ITEMS) {
|
|
|
|
|
+ history = history.slice(0, MAX_HISTORY_ITEMS);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Combine all queries of a datasource type into one history
|
|
|
|
|
+ const historyKey = `grafana.explore.history.${datasourceId}`;
|
|
|
|
|
+ store.setObject(historyKey, history);
|
|
|
|
|
+ return history;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
interface ExploreProps {
|
|
interface ExploreProps {
|
|
|
datasourceSrv: any;
|
|
datasourceSrv: any;
|
|
|
onChangeSplit: (split: boolean, state?: ExploreState) => void;
|
|
onChangeSplit: (split: boolean, state?: ExploreState) => void;
|
|
@@ -82,6 +100,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
} else {
|
|
} else {
|
|
|
const { datasource, queries, range } = props.urlState as ExploreUrlState;
|
|
const { datasource, queries, range } = props.urlState as ExploreUrlState;
|
|
|
initialQueries = ensureQueries(queries);
|
|
initialQueries = ensureQueries(queries);
|
|
|
|
|
+ const initialRange = range || { ...DEFAULT_RANGE };
|
|
|
this.state = {
|
|
this.state = {
|
|
|
datasource: null,
|
|
datasource: null,
|
|
|
datasourceError: null,
|
|
datasourceError: null,
|
|
@@ -89,23 +108,17 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
datasourceMissing: false,
|
|
datasourceMissing: false,
|
|
|
datasourceName: datasource,
|
|
datasourceName: datasource,
|
|
|
exploreDatasources: [],
|
|
exploreDatasources: [],
|
|
|
- graphResult: null,
|
|
|
|
|
|
|
+ graphRange: initialRange,
|
|
|
history: [],
|
|
history: [],
|
|
|
- latency: 0,
|
|
|
|
|
- loading: false,
|
|
|
|
|
- logsResult: null,
|
|
|
|
|
queries: initialQueries,
|
|
queries: initialQueries,
|
|
|
- queryErrors: [],
|
|
|
|
|
- queryHints: [],
|
|
|
|
|
- range: range || { ...DEFAULT_RANGE },
|
|
|
|
|
- requestOptions: null,
|
|
|
|
|
|
|
+ queryTransactions: [],
|
|
|
|
|
+ range: initialRange,
|
|
|
showingGraph: true,
|
|
showingGraph: true,
|
|
|
showingLogs: true,
|
|
showingLogs: true,
|
|
|
showingTable: true,
|
|
showingTable: true,
|
|
|
supportsGraph: null,
|
|
supportsGraph: null,
|
|
|
supportsLogs: null,
|
|
supportsLogs: null,
|
|
|
supportsTable: null,
|
|
supportsTable: null,
|
|
|
- tableResult: null,
|
|
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
this.queryExpressions = initialQueries.map(q => q.query);
|
|
this.queryExpressions = initialQueries.map(q => q.query);
|
|
@@ -199,14 +212,32 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onAddQueryRow = index => {
|
|
onAddQueryRow = index => {
|
|
|
- const { queries } = this.state;
|
|
|
|
|
|
|
+ // Local cache
|
|
|
this.queryExpressions[index + 1] = '';
|
|
this.queryExpressions[index + 1] = '';
|
|
|
- const nextQueries = [
|
|
|
|
|
- ...queries.slice(0, index + 1),
|
|
|
|
|
- { query: '', key: generateQueryKey() },
|
|
|
|
|
- ...queries.slice(index + 1),
|
|
|
|
|
- ];
|
|
|
|
|
- this.setState({ queries: nextQueries });
|
|
|
|
|
|
|
+
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ const { queries, queryTransactions } = state;
|
|
|
|
|
+
|
|
|
|
|
+ // Add row by generating new react key
|
|
|
|
|
+ const nextQueries = [
|
|
|
|
|
+ ...queries.slice(0, index + 1),
|
|
|
|
|
+ { query: '', key: generateQueryKey() },
|
|
|
|
|
+ ...queries.slice(index + 1),
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ // Ongoing transactions need to update their row indices
|
|
|
|
|
+ const nextQueryTransactions = queryTransactions.map(qt => {
|
|
|
|
|
+ if (qt.rowIndex > index) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...qt,
|
|
|
|
|
+ rowIndex: qt.rowIndex + 1,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return qt;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return { queries: nextQueries, queryTransactions: nextQueryTransactions };
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onChangeDatasource = async option => {
|
|
onChangeDatasource = async option => {
|
|
@@ -214,12 +245,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
datasource: null,
|
|
datasource: null,
|
|
|
datasourceError: null,
|
|
datasourceError: null,
|
|
|
datasourceLoading: true,
|
|
datasourceLoading: true,
|
|
|
- graphResult: null,
|
|
|
|
|
- latency: 0,
|
|
|
|
|
- logsResult: null,
|
|
|
|
|
- queryErrors: [],
|
|
|
|
|
- queryHints: [],
|
|
|
|
|
- tableResult: null,
|
|
|
|
|
|
|
+ queryTransactions: [],
|
|
|
});
|
|
});
|
|
|
const datasourceName = option.value;
|
|
const datasourceName = option.value;
|
|
|
const datasource = await this.props.datasourceSrv.get(datasourceName);
|
|
const datasource = await this.props.datasourceSrv.get(datasourceName);
|
|
@@ -230,24 +256,25 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
// Keep current value in local cache
|
|
// Keep current value in local cache
|
|
|
this.queryExpressions[index] = value;
|
|
this.queryExpressions[index] = value;
|
|
|
|
|
|
|
|
- // Replace query row on override
|
|
|
|
|
if (override) {
|
|
if (override) {
|
|
|
- const { queries } = this.state;
|
|
|
|
|
- const nextQuery: Query = {
|
|
|
|
|
- key: generateQueryKey(index),
|
|
|
|
|
- query: value,
|
|
|
|
|
- };
|
|
|
|
|
- const nextQueries = [...queries];
|
|
|
|
|
- nextQueries[index] = nextQuery;
|
|
|
|
|
-
|
|
|
|
|
- this.setState(
|
|
|
|
|
- {
|
|
|
|
|
- queryErrors: [],
|
|
|
|
|
- queryHints: [],
|
|
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ // Replace query row
|
|
|
|
|
+ const { queries, queryTransactions } = state;
|
|
|
|
|
+ const nextQuery: Query = {
|
|
|
|
|
+ key: generateQueryKey(index),
|
|
|
|
|
+ query: value,
|
|
|
|
|
+ };
|
|
|
|
|
+ const nextQueries = [...queries];
|
|
|
|
|
+ nextQueries[index] = nextQuery;
|
|
|
|
|
+
|
|
|
|
|
+ // Discard ongoing transaction related to row query
|
|
|
|
|
+ const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
queries: nextQueries,
|
|
queries: nextQueries,
|
|
|
- },
|
|
|
|
|
- this.onSubmit
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ }, this.onSubmit);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -263,13 +290,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
this.queryExpressions = [''];
|
|
this.queryExpressions = [''];
|
|
|
this.setState(
|
|
this.setState(
|
|
|
{
|
|
{
|
|
|
- graphResult: null,
|
|
|
|
|
- logsResult: null,
|
|
|
|
|
- latency: 0,
|
|
|
|
|
queries: ensureQueries(),
|
|
queries: ensureQueries(),
|
|
|
- queryErrors: [],
|
|
|
|
|
- queryHints: [],
|
|
|
|
|
- tableResult: null,
|
|
|
|
|
|
|
+ queryTransactions: [],
|
|
|
},
|
|
},
|
|
|
this.saveState
|
|
this.saveState
|
|
|
);
|
|
);
|
|
@@ -283,11 +305,41 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onClickGraphButton = () => {
|
|
onClickGraphButton = () => {
|
|
|
- this.setState(state => ({ showingGraph: !state.showingGraph }));
|
|
|
|
|
|
|
+ this.setState(
|
|
|
|
|
+ state => {
|
|
|
|
|
+ const showingGraph = !state.showingGraph;
|
|
|
|
|
+ let nextQueryTransactions = state.queryTransactions;
|
|
|
|
|
+ if (!showingGraph) {
|
|
|
|
|
+ // Discard transactions related to Graph query
|
|
|
|
|
+ nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Graph');
|
|
|
|
|
+ }
|
|
|
|
|
+ return { queryTransactions: nextQueryTransactions, showingGraph };
|
|
|
|
|
+ },
|
|
|
|
|
+ () => {
|
|
|
|
|
+ if (this.state.showingGraph) {
|
|
|
|
|
+ this.onSubmit();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onClickLogsButton = () => {
|
|
onClickLogsButton = () => {
|
|
|
- this.setState(state => ({ showingLogs: !state.showingLogs }));
|
|
|
|
|
|
|
+ this.setState(
|
|
|
|
|
+ state => {
|
|
|
|
|
+ const showingLogs = !state.showingLogs;
|
|
|
|
|
+ let nextQueryTransactions = state.queryTransactions;
|
|
|
|
|
+ if (!showingLogs) {
|
|
|
|
|
+ // Discard transactions related to Logs query
|
|
|
|
|
+ nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Logs');
|
|
|
|
|
+ }
|
|
|
|
|
+ return { queryTransactions: nextQueryTransactions, showingLogs };
|
|
|
|
|
+ },
|
|
|
|
|
+ () => {
|
|
|
|
|
+ if (this.state.showingLogs) {
|
|
|
|
|
+ this.onSubmit();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onClickSplit = () => {
|
|
onClickSplit = () => {
|
|
@@ -299,7 +351,22 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onClickTableButton = () => {
|
|
onClickTableButton = () => {
|
|
|
- this.setState(state => ({ showingTable: !state.showingTable }));
|
|
|
|
|
|
|
+ this.setState(
|
|
|
|
|
+ state => {
|
|
|
|
|
+ const showingTable = !state.showingTable;
|
|
|
|
|
+ let nextQueryTransactions = state.queryTransactions;
|
|
|
|
|
+ if (!showingTable) {
|
|
|
|
|
+ // Discard transactions related to Table query
|
|
|
|
|
+ nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Table');
|
|
|
|
|
+ }
|
|
|
|
|
+ return { queryTransactions: nextQueryTransactions, showingTable };
|
|
|
|
|
+ },
|
|
|
|
|
+ () => {
|
|
|
|
|
+ if (this.state.showingTable) {
|
|
|
|
|
+ this.onSubmit();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onClickTableCell = (columnKey: string, rowValue: string) => {
|
|
onClickTableCell = (columnKey: string, rowValue: string) => {
|
|
@@ -307,39 +374,68 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onModifyQueries = (action: object, index?: number) => {
|
|
onModifyQueries = (action: object, index?: number) => {
|
|
|
- const { datasource, queries } = this.state;
|
|
|
|
|
|
|
+ const { datasource } = this.state;
|
|
|
if (datasource && datasource.modifyQuery) {
|
|
if (datasource && datasource.modifyQuery) {
|
|
|
- let nextQueries;
|
|
|
|
|
- if (index === undefined) {
|
|
|
|
|
- // Modify all queries
|
|
|
|
|
- nextQueries = queries.map((q, i) => ({
|
|
|
|
|
- key: generateQueryKey(i),
|
|
|
|
|
- query: datasource.modifyQuery(this.queryExpressions[i], action),
|
|
|
|
|
- }));
|
|
|
|
|
- } else {
|
|
|
|
|
- // Modify query only at index
|
|
|
|
|
- nextQueries = [
|
|
|
|
|
- ...queries.slice(0, index),
|
|
|
|
|
- {
|
|
|
|
|
- key: generateQueryKey(index),
|
|
|
|
|
- query: datasource.modifyQuery(this.queryExpressions[index], action),
|
|
|
|
|
- },
|
|
|
|
|
- ...queries.slice(index + 1),
|
|
|
|
|
- ];
|
|
|
|
|
- }
|
|
|
|
|
- this.queryExpressions = nextQueries.map(q => q.query);
|
|
|
|
|
- this.setState({ queries: nextQueries }, () => this.onSubmit());
|
|
|
|
|
|
|
+ this.setState(
|
|
|
|
|
+ state => {
|
|
|
|
|
+ const { queries, queryTransactions } = state;
|
|
|
|
|
+ let nextQueries;
|
|
|
|
|
+ let nextQueryTransactions;
|
|
|
|
|
+ if (index === undefined) {
|
|
|
|
|
+ // Modify all queries
|
|
|
|
|
+ nextQueries = queries.map((q, i) => ({
|
|
|
|
|
+ key: generateQueryKey(i),
|
|
|
|
|
+ query: datasource.modifyQuery(this.queryExpressions[i], action),
|
|
|
|
|
+ }));
|
|
|
|
|
+ // Discard all ongoing transactions
|
|
|
|
|
+ nextQueryTransactions = [];
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Modify query only at index
|
|
|
|
|
+ nextQueries = [
|
|
|
|
|
+ ...queries.slice(0, index),
|
|
|
|
|
+ {
|
|
|
|
|
+ key: generateQueryKey(index),
|
|
|
|
|
+ query: datasource.modifyQuery(this.queryExpressions[index], action),
|
|
|
|
|
+ },
|
|
|
|
|
+ ...queries.slice(index + 1),
|
|
|
|
|
+ ];
|
|
|
|
|
+ // Discard transactions related to row query
|
|
|
|
|
+ nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.queryExpressions = nextQueries.map(q => q.query);
|
|
|
|
|
+ return {
|
|
|
|
|
+ queries: nextQueries,
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ () => this.onSubmit()
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onRemoveQueryRow = index => {
|
|
onRemoveQueryRow = index => {
|
|
|
- const { queries } = this.state;
|
|
|
|
|
- if (queries.length <= 1) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- const nextQueries = [...queries.slice(0, index), ...queries.slice(index + 1)];
|
|
|
|
|
- this.queryExpressions = nextQueries.map(q => q.query);
|
|
|
|
|
- this.setState({ queries: nextQueries }, () => this.onSubmit());
|
|
|
|
|
|
|
+ // Remove from local cache
|
|
|
|
|
+ this.queryExpressions = [...this.queryExpressions.slice(0, index), ...this.queryExpressions.slice(index + 1)];
|
|
|
|
|
+
|
|
|
|
|
+ this.setState(
|
|
|
|
|
+ state => {
|
|
|
|
|
+ const { queries, queryTransactions } = state;
|
|
|
|
|
+ if (queries.length <= 1) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Remove row from react state
|
|
|
|
|
+ const nextQueries = [...queries.slice(0, index), ...queries.slice(index + 1)];
|
|
|
|
|
+
|
|
|
|
|
+ // Discard transactions related to row query
|
|
|
|
|
+ const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ queries: nextQueries,
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ () => this.onSubmit()
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onSubmit = () => {
|
|
onSubmit = () => {
|
|
@@ -348,7 +444,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
this.runTableQuery();
|
|
this.runTableQuery();
|
|
|
}
|
|
}
|
|
|
if (showingGraph && supportsGraph) {
|
|
if (showingGraph && supportsGraph) {
|
|
|
- this.runGraphQuery();
|
|
|
|
|
|
|
+ this.runGraphQueries();
|
|
|
}
|
|
}
|
|
|
if (showingLogs && supportsLogs) {
|
|
if (showingLogs && supportsLogs) {
|
|
|
this.runLogsQuery();
|
|
this.runLogsQuery();
|
|
@@ -356,32 +452,11 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
this.saveState();
|
|
this.saveState();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- onQuerySuccess(datasourceId: string, queries: string[]): void {
|
|
|
|
|
- // save queries to history
|
|
|
|
|
- let { history } = this.state;
|
|
|
|
|
- const { datasource } = this.state;
|
|
|
|
|
-
|
|
|
|
|
- if (datasource.meta.id !== datasourceId) {
|
|
|
|
|
- // Navigated away, queries did not matter
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const ts = Date.now();
|
|
|
|
|
- queries.forEach(query => {
|
|
|
|
|
- history = [{ query, ts }, ...history];
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (history.length > MAX_HISTORY_ITEMS) {
|
|
|
|
|
- history = history.slice(0, MAX_HISTORY_ITEMS);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Combine all queries of a datasource type into one history
|
|
|
|
|
- const historyKey = `grafana.explore.history.${datasourceId}`;
|
|
|
|
|
- store.setObject(historyKey, history);
|
|
|
|
|
- this.setState({ history });
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- buildQueryOptions(targetOptions: { format: string; hinting?: boolean; instant?: boolean }) {
|
|
|
|
|
|
|
+ buildQueryOptions(
|
|
|
|
|
+ query: string,
|
|
|
|
|
+ rowIndex: number,
|
|
|
|
|
+ targetOptions: { format: string; hinting?: boolean; instant?: boolean }
|
|
|
|
|
+ ) {
|
|
|
const { datasource, range } = this.state;
|
|
const { datasource, range } = this.state;
|
|
|
const resolution = this.el.offsetWidth;
|
|
const resolution = this.el.offsetWidth;
|
|
|
const absoluteRange = {
|
|
const absoluteRange = {
|
|
@@ -389,88 +464,235 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
to: parseDate(range.to, true),
|
|
to: parseDate(range.to, true),
|
|
|
};
|
|
};
|
|
|
const { interval } = kbn.calculateInterval(absoluteRange, resolution, datasource.interval);
|
|
const { interval } = kbn.calculateInterval(absoluteRange, resolution, datasource.interval);
|
|
|
- const targets = this.queryExpressions.map(q => ({
|
|
|
|
|
- ...targetOptions,
|
|
|
|
|
- expr: q,
|
|
|
|
|
- }));
|
|
|
|
|
|
|
+ const targets = [
|
|
|
|
|
+ {
|
|
|
|
|
+ ...targetOptions,
|
|
|
|
|
+ // Target identifier is needed for table transformations
|
|
|
|
|
+ refId: rowIndex + 1,
|
|
|
|
|
+ expr: query,
|
|
|
|
|
+ },
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ // Clone range for query request
|
|
|
|
|
+ const queryRange: Range = { ...range };
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
interval,
|
|
interval,
|
|
|
- range,
|
|
|
|
|
targets,
|
|
targets,
|
|
|
|
|
+ range: queryRange,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async runGraphQuery() {
|
|
|
|
|
|
|
+ startQueryTransaction(query: string, rowIndex: number, resultType: ResultType, options: any): QueryTransaction {
|
|
|
|
|
+ const queryOptions = this.buildQueryOptions(query, rowIndex, options);
|
|
|
|
|
+ const transaction: QueryTransaction = {
|
|
|
|
|
+ query,
|
|
|
|
|
+ resultType,
|
|
|
|
|
+ rowIndex,
|
|
|
|
|
+ id: generateQueryKey(),
|
|
|
|
|
+ done: false,
|
|
|
|
|
+ latency: 0,
|
|
|
|
|
+ options: queryOptions,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Using updater style because we might be modifying queryTransactions in quick succession
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ const { queryTransactions } = state;
|
|
|
|
|
+ // Discarding existing transactions of same type
|
|
|
|
|
+ const remainingTransactions = queryTransactions.filter(
|
|
|
|
|
+ qt => !(qt.resultType === resultType && qt.rowIndex === rowIndex)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // Append new transaction
|
|
|
|
|
+ const nextQueryTransactions = [...remainingTransactions, transaction];
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return transaction;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ completeQueryTransaction(
|
|
|
|
|
+ transactionId: string,
|
|
|
|
|
+ result: any,
|
|
|
|
|
+ latency: number,
|
|
|
|
|
+ queries: string[],
|
|
|
|
|
+ datasourceId: string
|
|
|
|
|
+ ) {
|
|
|
const { datasource } = this.state;
|
|
const { datasource } = this.state;
|
|
|
|
|
+ if (datasource.meta.id !== datasourceId) {
|
|
|
|
|
+ // Navigated away, queries did not matter
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ const { history, queryTransactions } = state;
|
|
|
|
|
+
|
|
|
|
|
+ // Transaction might have been discarded
|
|
|
|
|
+ const transaction = queryTransactions.find(qt => qt.id === transactionId);
|
|
|
|
|
+ if (!transaction) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Get query hints
|
|
|
|
|
+ let hints;
|
|
|
|
|
+ if (datasource.getQueryHints) {
|
|
|
|
|
+ hints = datasource.getQueryHints(transaction.query, result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Mark transactions as complete
|
|
|
|
|
+ const nextQueryTransactions = queryTransactions.map(qt => {
|
|
|
|
|
+ if (qt.id === transactionId) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...qt,
|
|
|
|
|
+ hints,
|
|
|
|
|
+ latency,
|
|
|
|
|
+ result,
|
|
|
|
|
+ done: true,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return qt;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const nextHistory = updateHistory(history, datasourceId, queries);
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ history: nextHistory,
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ discardTransactions(rowIndex: number) {
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ const remainingTransactions = state.queryTransactions.filter(qt => qt.rowIndex !== rowIndex);
|
|
|
|
|
+ return { queryTransactions: remainingTransactions };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ failQueryTransaction(transactionId: string, error: string, datasourceId: string) {
|
|
|
|
|
+ const { datasource } = this.state;
|
|
|
|
|
+ if (datasource.meta.id !== datasourceId) {
|
|
|
|
|
+ // Navigated away, queries did not matter
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.setState(state => {
|
|
|
|
|
+ // Transaction might have been discarded
|
|
|
|
|
+ if (!state.queryTransactions.find(qt => qt.id === transactionId)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Mark transactions as complete
|
|
|
|
|
+ const nextQueryTransactions = state.queryTransactions.map(qt => {
|
|
|
|
|
+ if (qt.id === transactionId) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...qt,
|
|
|
|
|
+ error,
|
|
|
|
|
+ done: true,
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+ return qt;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ queryTransactions: nextQueryTransactions,
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async runGraphQueries() {
|
|
|
const queries = [...this.queryExpressions];
|
|
const queries = [...this.queryExpressions];
|
|
|
if (!hasQuery(queries)) {
|
|
if (!hasQuery(queries)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.setState({ latency: 0, loading: true, graphResult: null, queryErrors: [], queryHints: [] });
|
|
|
|
|
- const now = Date.now();
|
|
|
|
|
- const options = this.buildQueryOptions({ format: 'time_series', instant: false, hinting: true });
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await datasource.query(options);
|
|
|
|
|
- const result = makeTimeSeriesList(res.data, options);
|
|
|
|
|
- const queryHints = res.hints ? makeHints(res.hints) : [];
|
|
|
|
|
- const latency = Date.now() - now;
|
|
|
|
|
- this.setState({ latency, loading: false, graphResult: result, queryHints, requestOptions: options });
|
|
|
|
|
- this.onQuerySuccess(datasource.meta.id, queries);
|
|
|
|
|
- } catch (response) {
|
|
|
|
|
- console.error(response);
|
|
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
|
|
- this.setState({ loading: false, queryErrors: [queryError] });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const { datasource } = this.state;
|
|
|
|
|
+ const datasourceId = datasource.meta.id;
|
|
|
|
|
+ // Run all queries concurrently
|
|
|
|
|
+ queries.forEach(async (query, rowIndex) => {
|
|
|
|
|
+ if (query) {
|
|
|
|
|
+ const transaction = this.startQueryTransaction(query, rowIndex, 'Graph', {
|
|
|
|
|
+ format: 'time_series',
|
|
|
|
|
+ instant: false,
|
|
|
|
|
+ });
|
|
|
|
|
+ try {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ const res = await datasource.query(transaction.options);
|
|
|
|
|
+ const latency = Date.now() - now;
|
|
|
|
|
+ const results = makeTimeSeriesList(res.data, transaction.options);
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.discardTransactions(rowIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async runTableQuery() {
|
|
async runTableQuery() {
|
|
|
const queries = [...this.queryExpressions];
|
|
const queries = [...this.queryExpressions];
|
|
|
- const { datasource } = this.state;
|
|
|
|
|
if (!hasQuery(queries)) {
|
|
if (!hasQuery(queries)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.setState({ latency: 0, loading: true, queryErrors: [], queryHints: [], tableResult: null });
|
|
|
|
|
- const now = Date.now();
|
|
|
|
|
- const options = this.buildQueryOptions({
|
|
|
|
|
- format: 'table',
|
|
|
|
|
- instant: true,
|
|
|
|
|
|
|
+ const { datasource } = this.state;
|
|
|
|
|
+ const datasourceId = datasource.meta.id;
|
|
|
|
|
+ // Run all queries concurrently
|
|
|
|
|
+ queries.forEach(async (query, rowIndex) => {
|
|
|
|
|
+ if (query) {
|
|
|
|
|
+ const transaction = this.startQueryTransaction(query, rowIndex, 'Table', {
|
|
|
|
|
+ format: 'table',
|
|
|
|
|
+ instant: true,
|
|
|
|
|
+ valueWithRefId: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ try {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ const res = await datasource.query(transaction.options);
|
|
|
|
|
+ const latency = Date.now() - now;
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.discardTransactions(rowIndex);
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
- try {
|
|
|
|
|
- const res = await datasource.query(options);
|
|
|
|
|
- const tableModel = res.data[0];
|
|
|
|
|
- const latency = Date.now() - now;
|
|
|
|
|
- this.setState({ latency, loading: false, tableResult: tableModel, requestOptions: options });
|
|
|
|
|
- this.onQuerySuccess(datasource.meta.id, queries);
|
|
|
|
|
- } catch (response) {
|
|
|
|
|
- console.error(response);
|
|
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
|
|
- this.setState({ loading: false, queryErrors: [queryError] });
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async runLogsQuery() {
|
|
async runLogsQuery() {
|
|
|
const queries = [...this.queryExpressions];
|
|
const queries = [...this.queryExpressions];
|
|
|
- const { datasource } = this.state;
|
|
|
|
|
if (!hasQuery(queries)) {
|
|
if (!hasQuery(queries)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.setState({ latency: 0, loading: true, queryErrors: [], queryHints: [], logsResult: null });
|
|
|
|
|
- const now = Date.now();
|
|
|
|
|
- const options = this.buildQueryOptions({
|
|
|
|
|
- format: 'logs',
|
|
|
|
|
|
|
+ const { datasource } = this.state;
|
|
|
|
|
+ const datasourceId = datasource.meta.id;
|
|
|
|
|
+ // Run all queries concurrently
|
|
|
|
|
+ queries.forEach(async (query, rowIndex) => {
|
|
|
|
|
+ if (query) {
|
|
|
|
|
+ const transaction = this.startQueryTransaction(query, rowIndex, 'Logs', { format: 'logs' });
|
|
|
|
|
+ try {
|
|
|
|
|
+ const now = Date.now();
|
|
|
|
|
+ const res = await datasource.query(transaction.options);
|
|
|
|
|
+ const latency = Date.now() - now;
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.discardTransactions(rowIndex);
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await datasource.query(options);
|
|
|
|
|
- const logsData = res.data;
|
|
|
|
|
- const latency = Date.now() - now;
|
|
|
|
|
- this.setState({ latency, loading: false, logsResult: logsData, requestOptions: options });
|
|
|
|
|
- this.onQuerySuccess(datasource.meta.id, queries);
|
|
|
|
|
- } catch (response) {
|
|
|
|
|
- console.error(response);
|
|
|
|
|
- const queryError = response.data ? response.data.error : response;
|
|
|
|
|
- this.setState({ loading: false, queryErrors: [queryError] });
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
request = url => {
|
|
request = url => {
|
|
@@ -482,6 +704,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
// Copy state, but copy queries including modifications
|
|
// Copy state, but copy queries including modifications
|
|
|
return {
|
|
return {
|
|
|
...this.state,
|
|
...this.state,
|
|
|
|
|
+ queryTransactions: [],
|
|
|
queries: ensureQueries(this.queryExpressions.map(query => ({ query }))),
|
|
queries: ensureQueries(this.queryExpressions.map(query => ({ query }))),
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
@@ -499,23 +722,17 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
datasourceLoading,
|
|
datasourceLoading,
|
|
|
datasourceMissing,
|
|
datasourceMissing,
|
|
|
exploreDatasources,
|
|
exploreDatasources,
|
|
|
- graphResult,
|
|
|
|
|
|
|
+ graphRange,
|
|
|
history,
|
|
history,
|
|
|
- latency,
|
|
|
|
|
- loading,
|
|
|
|
|
- logsResult,
|
|
|
|
|
queries,
|
|
queries,
|
|
|
- queryErrors,
|
|
|
|
|
- queryHints,
|
|
|
|
|
|
|
+ queryTransactions,
|
|
|
range,
|
|
range,
|
|
|
- requestOptions,
|
|
|
|
|
showingGraph,
|
|
showingGraph,
|
|
|
showingLogs,
|
|
showingLogs,
|
|
|
showingTable,
|
|
showingTable,
|
|
|
supportsGraph,
|
|
supportsGraph,
|
|
|
supportsLogs,
|
|
supportsLogs,
|
|
|
supportsTable,
|
|
supportsTable,
|
|
|
- tableResult,
|
|
|
|
|
} = this.state;
|
|
} = this.state;
|
|
|
const showingBoth = showingGraph && showingTable;
|
|
const showingBoth = showingGraph && showingTable;
|
|
|
const graphHeight = showingBoth ? '200px' : '400px';
|
|
const graphHeight = showingBoth ? '200px' : '400px';
|
|
@@ -524,6 +741,20 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
const tableButtonActive = showingBoth || showingTable ? 'active' : '';
|
|
const tableButtonActive = showingBoth || showingTable ? 'active' : '';
|
|
|
const exploreClass = split ? 'explore explore-split' : 'explore';
|
|
const exploreClass = split ? 'explore explore-split' : 'explore';
|
|
|
const selectedDatasource = datasource ? exploreDatasources.find(d => d.label === datasource.name) : undefined;
|
|
const selectedDatasource = datasource ? exploreDatasources.find(d => d.label === datasource.name) : undefined;
|
|
|
|
|
+ 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);
|
|
|
|
|
+ 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)
|
|
|
|
|
+ );
|
|
|
|
|
+ const logsResult = _.flatten(
|
|
|
|
|
+ queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done).map(qt => qt.result)
|
|
|
|
|
+ );
|
|
|
|
|
+ const loading = queryTransactions.some(qt => !qt.done);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<div className={exploreClass} ref={this.getRef}>
|
|
<div className={exploreClass} ref={this.getRef}>
|
|
@@ -581,9 +812,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
</div>
|
|
</div>
|
|
|
<div className="navbar-buttons relative">
|
|
<div className="navbar-buttons relative">
|
|
|
<button className="btn navbar-button--primary" onClick={this.onSubmit}>
|
|
<button className="btn navbar-button--primary" onClick={this.onSubmit}>
|
|
|
- Run Query <i className="fa fa-level-down run-icon" />
|
|
|
|
|
|
|
+ Run Query{' '}
|
|
|
|
|
+ {loading ? <i className="fa fa-spinner fa-spin run-icon" /> : <i className="fa fa-level-down run-icon" />}
|
|
|
</button>
|
|
</button>
|
|
|
- {loading || latency ? <ElapsedTime time={latency} className="text-info" /> : null}
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -602,8 +833,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
<QueryRows
|
|
<QueryRows
|
|
|
history={history}
|
|
history={history}
|
|
|
queries={queries}
|
|
queries={queries}
|
|
|
- queryErrors={queryErrors}
|
|
|
|
|
- queryHints={queryHints}
|
|
|
|
|
request={this.request}
|
|
request={this.request}
|
|
|
onAddQueryRow={this.onAddQueryRow}
|
|
onAddQueryRow={this.onAddQueryRow}
|
|
|
onChangeQuery={this.onChangeQuery}
|
|
onChangeQuery={this.onChangeQuery}
|
|
@@ -611,6 +840,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
onExecuteQuery={this.onSubmit}
|
|
onExecuteQuery={this.onSubmit}
|
|
|
onRemoveQueryRow={this.onRemoveQueryRow}
|
|
onRemoveQueryRow={this.onRemoveQueryRow}
|
|
|
supportsLogs={supportsLogs}
|
|
supportsLogs={supportsLogs}
|
|
|
|
|
+ transactions={queryTransactions}
|
|
|
/>
|
|
/>
|
|
|
<div className="result-options">
|
|
<div className="result-options">
|
|
|
{supportsGraph ? (
|
|
{supportsGraph ? (
|
|
@@ -632,23 +862,22 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
|
|
|
|
|
|
|
<main className="m-t-2">
|
|
<main className="m-t-2">
|
|
|
{supportsGraph &&
|
|
{supportsGraph &&
|
|
|
- showingGraph &&
|
|
|
|
|
- graphResult && (
|
|
|
|
|
|
|
+ showingGraph && (
|
|
|
<Graph
|
|
<Graph
|
|
|
data={graphResult}
|
|
data={graphResult}
|
|
|
height={graphHeight}
|
|
height={graphHeight}
|
|
|
- loading={loading}
|
|
|
|
|
|
|
+ loading={graphLoading}
|
|
|
id={`explore-graph-${position}`}
|
|
id={`explore-graph-${position}`}
|
|
|
- options={requestOptions}
|
|
|
|
|
|
|
+ range={graphRange}
|
|
|
split={split}
|
|
split={split}
|
|
|
/>
|
|
/>
|
|
|
)}
|
|
)}
|
|
|
{supportsTable && showingTable ? (
|
|
{supportsTable && showingTable ? (
|
|
|
- <div className="panel-container">
|
|
|
|
|
- <Table data={tableResult} loading={loading} onClickCell={this.onClickTableCell} />
|
|
|
|
|
|
|
+ <div className="panel-container m-t-2">
|
|
|
|
|
+ <Table data={tableResult} loading={tableLoading} onClickCell={this.onClickTableCell} />
|
|
|
</div>
|
|
</div>
|
|
|
) : null}
|
|
) : null}
|
|
|
- {supportsLogs && showingLogs ? <Logs data={logsResult} loading={loading} /> : null}
|
|
|
|
|
|
|
+ {supportsLogs && showingLogs ? <Logs data={logsResult} loading={logsLoading} /> : null}
|
|
|
</main>
|
|
</main>
|
|
|
</div>
|
|
</div>
|
|
|
) : null}
|
|
) : null}
|