mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Allow multiple Explore items for split
This commit is contained in:
parent
2be2deddb8
commit
68c039b289
@ -206,11 +206,14 @@ export function ensureQueries(queries?: DataQuery[]): DataQuery[] {
|
||||
* A target is non-empty when it has keys (with non-empty values) other than refId and key.
|
||||
*/
|
||||
export function hasNonEmptyQuery(queries: DataQuery[]): boolean {
|
||||
return queries.some(
|
||||
query =>
|
||||
Object.keys(query)
|
||||
.map(k => query[k])
|
||||
.filter(v => v).length > 2
|
||||
return (
|
||||
queries &&
|
||||
queries.some(
|
||||
query =>
|
||||
Object.keys(query)
|
||||
.map(k => query[k])
|
||||
.filter(v => v).length > 2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,15 @@ import React from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import _ from 'lodash';
|
||||
import { withSize } from 'react-sizeme';
|
||||
import { AutoSizer } from 'react-virtualized';
|
||||
import { RawTimeRange, TimeRange } from '@grafana/ui';
|
||||
|
||||
import { DataSourceSelectItem } from 'app/types/datasources';
|
||||
import { ExploreUrlState, HistoryItem, QueryTransaction, RangeScanner } from 'app/types/explore';
|
||||
import { ExploreUrlState, HistoryItem, QueryTransaction, RangeScanner, ExploreId } from 'app/types/explore';
|
||||
import { DataQuery } from 'app/types/series';
|
||||
import { StoreState } from 'app/types';
|
||||
import store from 'app/core/store';
|
||||
import { LAST_USED_DATASOURCE_KEY, ensureQueries } from 'app/core/utils/explore';
|
||||
import { LAST_USED_DATASOURCE_KEY, ensureQueries, DEFAULT_RANGE } from 'app/core/utils/explore';
|
||||
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
|
||||
import { Emitter } from 'app/core/utils/emitter';
|
||||
|
||||
@ -20,9 +21,11 @@ import {
|
||||
changeSize,
|
||||
changeTime,
|
||||
clickClear,
|
||||
clickCloseSplit,
|
||||
clickExample,
|
||||
clickGraphButton,
|
||||
clickLogsButton,
|
||||
clickSplit,
|
||||
clickTableButton,
|
||||
highlightLogsExpression,
|
||||
initializeExplore,
|
||||
@ -32,7 +35,7 @@ import {
|
||||
scanStart,
|
||||
scanStop,
|
||||
} from './state/actions';
|
||||
import { ExploreState } from './state/reducers';
|
||||
import { ExploreItemState } from './state/reducers';
|
||||
|
||||
import Panel from './Panel';
|
||||
import QueryRows from './QueryRows';
|
||||
@ -50,17 +53,21 @@ interface ExploreProps {
|
||||
addQueryRow: typeof addQueryRow;
|
||||
changeDatasource: typeof changeDatasource;
|
||||
changeQuery: typeof changeQuery;
|
||||
changeSize: typeof changeSize;
|
||||
changeTime: typeof changeTime;
|
||||
clickClear: typeof clickClear;
|
||||
clickCloseSplit: typeof clickCloseSplit;
|
||||
clickExample: typeof clickExample;
|
||||
clickGraphButton: typeof clickGraphButton;
|
||||
clickLogsButton: typeof clickLogsButton;
|
||||
clickSplit: typeof clickSplit;
|
||||
clickTableButton: typeof clickTableButton;
|
||||
datasourceError: string;
|
||||
datasourceInstance: any;
|
||||
datasourceLoading: boolean | null;
|
||||
datasourceMissing: boolean;
|
||||
exploreDatasources: DataSourceSelectItem[];
|
||||
exploreId: ExploreId;
|
||||
graphResult?: any[];
|
||||
highlightLogsExpression: typeof highlightLogsExpression;
|
||||
history: HistoryItem[];
|
||||
@ -70,9 +77,6 @@ interface ExploreProps {
|
||||
logsHighlighterExpressions?: string[];
|
||||
logsResult?: LogsModel;
|
||||
modifyQueries: typeof modifyQueries;
|
||||
onChangeSplit: (split: boolean, state?: ExploreState) => void;
|
||||
onSaveState: (key: string, state: ExploreState) => void;
|
||||
position: string;
|
||||
queryTransactions: QueryTransaction[];
|
||||
removeQueryRow: typeof removeQueryRow;
|
||||
range: RawTimeRange;
|
||||
@ -83,8 +87,6 @@ interface ExploreProps {
|
||||
scanStart: typeof scanStart;
|
||||
scanStop: typeof scanStop;
|
||||
split: boolean;
|
||||
splitState?: ExploreState;
|
||||
stateKey: string;
|
||||
showingGraph: boolean;
|
||||
showingLogs: boolean;
|
||||
showingStartPage?: boolean;
|
||||
@ -132,7 +134,7 @@ interface ExploreProps {
|
||||
* The result viewers determine some of the query options sent to the datasource, e.g.,
|
||||
* `format`, to indicate eventual transformations by the datasources' result transformers.
|
||||
*/
|
||||
export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
export class Explore extends React.PureComponent<ExploreProps> {
|
||||
el: any;
|
||||
exploreEvents: Emitter;
|
||||
/**
|
||||
@ -147,13 +149,23 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Load URL state and parse range
|
||||
const { datasource, queries, range } = this.props.urlState as ExploreUrlState;
|
||||
const initialDatasource = datasource || store.get(LAST_USED_DATASOURCE_KEY);
|
||||
const initialQueries: DataQuery[] = ensureQueries(queries);
|
||||
const initialRange = { from: parseTime(range.from), to: parseTime(range.to) };
|
||||
const width = this.el ? this.el.offsetWidth : 0;
|
||||
this.props.initializeExplore(initialDatasource, initialQueries, initialRange, width, this.exploreEvents);
|
||||
const { exploreId, split, urlState } = this.props;
|
||||
if (!split) {
|
||||
// Load URL state and parse range
|
||||
const { datasource, queries, range = DEFAULT_RANGE } = (urlState || {}) as ExploreUrlState;
|
||||
const initialDatasource = datasource || store.get(LAST_USED_DATASOURCE_KEY);
|
||||
const initialQueries: DataQuery[] = ensureQueries(queries);
|
||||
const initialRange = { from: parseTime(range.from), to: parseTime(range.to) };
|
||||
const width = this.el ? this.el.offsetWidth : 0;
|
||||
this.props.initializeExplore(
|
||||
exploreId,
|
||||
initialDatasource,
|
||||
initialQueries,
|
||||
initialRange,
|
||||
width,
|
||||
this.exploreEvents
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@ -165,17 +177,17 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
};
|
||||
|
||||
onAddQueryRow = index => {
|
||||
this.props.addQueryRow(index);
|
||||
this.props.addQueryRow(this.props.exploreId, index);
|
||||
};
|
||||
|
||||
onChangeDatasource = async option => {
|
||||
this.props.changeDatasource(option.value);
|
||||
this.props.changeDatasource(this.props.exploreId, option.value);
|
||||
};
|
||||
|
||||
onChangeQuery = (query: DataQuery, index: number, override?: boolean) => {
|
||||
const { changeQuery, datasourceInstance } = this.props;
|
||||
const { changeQuery, datasourceInstance, exploreId } = this.props;
|
||||
|
||||
changeQuery(query, index, override);
|
||||
changeQuery(exploreId, query, index, override);
|
||||
if (query && !override && datasourceInstance.getHighlighterExpression && index === 0) {
|
||||
// Live preview of log search matches. Only use on first row for now
|
||||
this.updateLogsHighlights(query);
|
||||
@ -186,43 +198,36 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
if (this.props.scanning && !changedByScanner) {
|
||||
this.onStopScanning();
|
||||
}
|
||||
this.props.changeTime(range);
|
||||
this.props.changeTime(this.props.exploreId, range);
|
||||
};
|
||||
|
||||
onClickClear = () => {
|
||||
this.props.clickClear();
|
||||
this.props.clickClear(this.props.exploreId);
|
||||
};
|
||||
|
||||
onClickCloseSplit = () => {
|
||||
const { onChangeSplit } = this.props;
|
||||
if (onChangeSplit) {
|
||||
onChangeSplit(false);
|
||||
}
|
||||
this.props.clickCloseSplit();
|
||||
};
|
||||
|
||||
onClickGraphButton = () => {
|
||||
this.props.clickGraphButton();
|
||||
this.props.clickGraphButton(this.props.exploreId);
|
||||
};
|
||||
|
||||
onClickLogsButton = () => {
|
||||
this.props.clickLogsButton();
|
||||
this.props.clickLogsButton(this.props.exploreId);
|
||||
};
|
||||
|
||||
// Use this in help pages to set page to a single query
|
||||
onClickExample = (query: DataQuery) => {
|
||||
this.props.clickExample(query);
|
||||
this.props.clickExample(this.props.exploreId, query);
|
||||
};
|
||||
|
||||
onClickSplit = () => {
|
||||
const { onChangeSplit } = this.props;
|
||||
if (onChangeSplit) {
|
||||
// const state = this.cloneState();
|
||||
// onChangeSplit(true, state);
|
||||
}
|
||||
this.props.clickSplit();
|
||||
};
|
||||
|
||||
onClickTableButton = () => {
|
||||
this.props.clickTableButton();
|
||||
this.props.clickTableButton(this.props.exploreId);
|
||||
};
|
||||
|
||||
onClickLabel = (key: string, value: string) => {
|
||||
@ -233,18 +238,22 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
const { datasourceInstance } = this.props;
|
||||
if (datasourceInstance && datasourceInstance.modifyQuery) {
|
||||
const modifier = (queries: DataQuery, action: any) => datasourceInstance.modifyQuery(queries, action);
|
||||
this.props.modifyQueries(action, index, modifier);
|
||||
this.props.modifyQueries(this.props.exploreId, action, index, modifier);
|
||||
}
|
||||
};
|
||||
|
||||
onRemoveQueryRow = index => {
|
||||
this.props.removeQueryRow(index);
|
||||
this.props.removeQueryRow(this.props.exploreId, index);
|
||||
};
|
||||
|
||||
onResize = (size: { height: number; width: number }) => {
|
||||
this.props.changeSize(this.props.exploreId, size);
|
||||
};
|
||||
|
||||
onStartScanning = () => {
|
||||
// Scanner will trigger a query
|
||||
const scanner = this.scanPreviousRange;
|
||||
this.props.scanStart(scanner);
|
||||
this.props.scanStart(this.props.exploreId, scanner);
|
||||
};
|
||||
|
||||
scanPreviousRange = (): RawTimeRange => {
|
||||
@ -253,30 +262,21 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
};
|
||||
|
||||
onStopScanning = () => {
|
||||
this.props.scanStop();
|
||||
this.props.scanStop(this.props.exploreId);
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
this.props.runQueries();
|
||||
this.props.runQueries(this.props.exploreId);
|
||||
};
|
||||
|
||||
updateLogsHighlights = _.debounce((value: DataQuery) => {
|
||||
const { datasourceInstance } = this.props;
|
||||
if (datasourceInstance.getHighlighterExpression) {
|
||||
const expressions = [datasourceInstance.getHighlighterExpression(value)];
|
||||
this.props.highlightLogsExpression(expressions);
|
||||
this.props.highlightLogsExpression(this.props.exploreId, expressions);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
// cloneState(): ExploreState {
|
||||
// // Copy state, but copy queries including modifications
|
||||
// return {
|
||||
// ...this.state,
|
||||
// queryTransactions: [],
|
||||
// initialQueries: [...this.modifiedQueries],
|
||||
// };
|
||||
// }
|
||||
|
||||
// saveState = () => {
|
||||
// const { stateKey, onSaveState } = this.props;
|
||||
// onSaveState(stateKey, this.cloneState());
|
||||
@ -290,13 +290,13 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
datasourceLoading,
|
||||
datasourceMissing,
|
||||
exploreDatasources,
|
||||
exploreId,
|
||||
graphResult,
|
||||
history,
|
||||
initialQueries,
|
||||
logsHighlighterExpressions,
|
||||
logsResult,
|
||||
queryTransactions,
|
||||
position,
|
||||
range,
|
||||
scanning,
|
||||
scanRange,
|
||||
@ -323,7 +323,7 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
return (
|
||||
<div className={exploreClass} ref={this.getRef}>
|
||||
<div className="navbar">
|
||||
{position === 'left' ? (
|
||||
{exploreId === 'left' ? (
|
||||
<div>
|
||||
<a className="navbar-page-btn">
|
||||
<i className="fa fa-rocket" />
|
||||
@ -347,7 +347,7 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
</div>
|
||||
) : null}
|
||||
<div className="navbar__spacer" />
|
||||
{position === 'left' && !split ? (
|
||||
{exploreId === 'left' && !split ? (
|
||||
<div className="navbar-buttons">
|
||||
<button className="btn navbar-button" onClick={this.onClickSplit}>
|
||||
Split
|
||||
@ -378,83 +378,96 @@ export class Explore extends React.PureComponent<ExploreProps, any> {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{datasourceInstance && !datasourceError ? (
|
||||
<div className="explore-container">
|
||||
<QueryRows
|
||||
datasource={datasourceInstance}
|
||||
history={history}
|
||||
initialQueries={initialQueries}
|
||||
onAddQueryRow={this.onAddQueryRow}
|
||||
onChangeQuery={this.onChangeQuery}
|
||||
onClickHintFix={this.onModifyQueries}
|
||||
onExecuteQuery={this.onSubmit}
|
||||
onRemoveQueryRow={this.onRemoveQueryRow}
|
||||
transactions={queryTransactions}
|
||||
exploreEvents={this.exploreEvents}
|
||||
range={range}
|
||||
/>
|
||||
<main className="m-t-2">
|
||||
<ErrorBoundary>
|
||||
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
|
||||
{!showingStartPage && (
|
||||
<>
|
||||
{supportsGraph && (
|
||||
<Panel
|
||||
label="Graph"
|
||||
isOpen={showingGraph}
|
||||
loading={graphLoading}
|
||||
onToggle={this.onClickGraphButton}
|
||||
>
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
id={`explore-graph-${position}`}
|
||||
onChangeTime={this.onChangeTime}
|
||||
range={range}
|
||||
split={split}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
{supportsTable && (
|
||||
<Panel
|
||||
label="Table"
|
||||
loading={tableLoading}
|
||||
isOpen={showingTable}
|
||||
onToggle={this.onClickTableButton}
|
||||
>
|
||||
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickLabel} />
|
||||
</Panel>
|
||||
)}
|
||||
{supportsLogs && (
|
||||
<Panel label="Logs" loading={logsLoading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
|
||||
<Logs
|
||||
data={logsResult}
|
||||
key={logsResult.id}
|
||||
highlighterExpressions={logsHighlighterExpressions}
|
||||
loading={logsLoading}
|
||||
position={position}
|
||||
onChangeTime={this.onChangeTime}
|
||||
onClickLabel={this.onClickLabel}
|
||||
onStartScanning={this.onStartScanning}
|
||||
onStopScanning={this.onStopScanning}
|
||||
range={range}
|
||||
scanning={scanning}
|
||||
scanRange={scanRange}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
</>
|
||||
{datasourceInstance &&
|
||||
!datasourceError && (
|
||||
<div className="explore-container">
|
||||
<QueryRows
|
||||
datasource={datasourceInstance}
|
||||
history={history}
|
||||
initialQueries={initialQueries}
|
||||
onAddQueryRow={this.onAddQueryRow}
|
||||
onChangeQuery={this.onChangeQuery}
|
||||
onClickHintFix={this.onModifyQueries}
|
||||
onExecuteQuery={this.onSubmit}
|
||||
onRemoveQueryRow={this.onRemoveQueryRow}
|
||||
transactions={queryTransactions}
|
||||
exploreEvents={this.exploreEvents}
|
||||
range={range}
|
||||
/>
|
||||
<AutoSizer onResize={this.onResize} disableHeight>
|
||||
{({ width }) => (
|
||||
<main className="m-t-2" style={{ width }}>
|
||||
<ErrorBoundary>
|
||||
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
|
||||
{!showingStartPage && (
|
||||
<>
|
||||
{supportsGraph && (
|
||||
<Panel
|
||||
label="Graph"
|
||||
isOpen={showingGraph}
|
||||
loading={graphLoading}
|
||||
onToggle={this.onClickGraphButton}
|
||||
>
|
||||
<Graph
|
||||
data={graphResult}
|
||||
height={graphHeight}
|
||||
id={`explore-graph-${exploreId}`}
|
||||
onChangeTime={this.onChangeTime}
|
||||
range={range}
|
||||
split={split}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
{supportsTable && (
|
||||
<Panel
|
||||
label="Table"
|
||||
loading={tableLoading}
|
||||
isOpen={showingTable}
|
||||
onToggle={this.onClickTableButton}
|
||||
>
|
||||
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickLabel} />
|
||||
</Panel>
|
||||
)}
|
||||
{supportsLogs && (
|
||||
<Panel
|
||||
label="Logs"
|
||||
loading={logsLoading}
|
||||
isOpen={showingLogs}
|
||||
onToggle={this.onClickLogsButton}
|
||||
>
|
||||
<Logs
|
||||
data={logsResult}
|
||||
exploreId={exploreId}
|
||||
key={logsResult.id}
|
||||
highlighterExpressions={logsHighlighterExpressions}
|
||||
loading={logsLoading}
|
||||
onChangeTime={this.onChangeTime}
|
||||
onClickLabel={this.onClickLabel}
|
||||
onStartScanning={this.onStartScanning}
|
||||
onStopScanning={this.onStopScanning}
|
||||
range={range}
|
||||
scanning={scanning}
|
||||
scanRange={scanRange}
|
||||
/>
|
||||
</Panel>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</main>
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
</main>
|
||||
</div>
|
||||
) : null}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps({ explore }) {
|
||||
function mapStateToProps(state: StoreState, { exploreId }) {
|
||||
const explore = state.explore;
|
||||
const { split } = explore;
|
||||
const item: ExploreItemState = explore[exploreId];
|
||||
const {
|
||||
StartPage,
|
||||
datasourceError,
|
||||
@ -480,7 +493,7 @@ function mapStateToProps({ explore }) {
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
tableResult,
|
||||
} = explore as ExploreState;
|
||||
} = item;
|
||||
return {
|
||||
StartPage,
|
||||
datasourceError,
|
||||
@ -502,6 +515,7 @@ function mapStateToProps({ explore }) {
|
||||
showingLogs,
|
||||
showingStartPage,
|
||||
showingTable,
|
||||
split,
|
||||
supportsGraph,
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
@ -513,20 +527,22 @@ const mapDispatchToProps = {
|
||||
addQueryRow,
|
||||
changeDatasource,
|
||||
changeQuery,
|
||||
changeSize,
|
||||
changeTime,
|
||||
clickClear,
|
||||
clickCloseSplit,
|
||||
clickExample,
|
||||
clickGraphButton,
|
||||
clickLogsButton,
|
||||
clickSplit,
|
||||
clickTableButton,
|
||||
highlightLogsExpression,
|
||||
initializeExplore,
|
||||
modifyQueries,
|
||||
onSize: changeSize, // used by withSize HOC
|
||||
removeQueryRow,
|
||||
runQueries,
|
||||
scanStart,
|
||||
scanStop,
|
||||
};
|
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(withSize()(Explore)));
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(Explore));
|
||||
|
@ -241,9 +241,9 @@ function renderMetaItem(value: any, kind: LogsMetaKind) {
|
||||
|
||||
interface LogsProps {
|
||||
data: LogsModel;
|
||||
exploreId: string;
|
||||
highlighterExpressions: string[];
|
||||
loading: boolean;
|
||||
position: string;
|
||||
range?: RawTimeRange;
|
||||
scanning?: boolean;
|
||||
scanRange?: RawTimeRange;
|
||||
@ -348,10 +348,10 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
|
||||
render() {
|
||||
const {
|
||||
data,
|
||||
exploreId,
|
||||
highlighterExpressions,
|
||||
loading = false,
|
||||
onClickLabel,
|
||||
position,
|
||||
range,
|
||||
scanning,
|
||||
scanRange,
|
||||
@ -400,7 +400,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
|
||||
data={data.series}
|
||||
height="100px"
|
||||
range={range}
|
||||
id={`explore-logs-graph-${position}`}
|
||||
id={`explore-logs-graph-${exploreId}`}
|
||||
onChangeTime={this.props.onChangeTime}
|
||||
onToggleSeries={this.onToggleLogLevel}
|
||||
userOptions={graphOptions}
|
||||
|
@ -3,9 +3,9 @@ import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { serializeStateToUrlParam, parseUrlState } from 'app/core/utils/explore';
|
||||
// import { serializeStateToUrlParam, parseUrlState } from 'app/core/utils/explore';
|
||||
import { StoreState } from 'app/types';
|
||||
import { ExploreState } from 'app/types/explore';
|
||||
import { ExploreId } from 'app/types/explore';
|
||||
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import Explore from './Explore';
|
||||
@ -13,81 +13,41 @@ import Explore from './Explore';
|
||||
interface WrapperProps {
|
||||
backendSrv?: any;
|
||||
datasourceSrv?: any;
|
||||
updateLocation: typeof updateLocation;
|
||||
urlStates: { [key: string]: string };
|
||||
}
|
||||
|
||||
interface WrapperState {
|
||||
split: boolean;
|
||||
splitState: ExploreState;
|
||||
updateLocation: typeof updateLocation;
|
||||
// urlStates: { [key: string]: string };
|
||||
}
|
||||
|
||||
const STATE_KEY_LEFT = 'state';
|
||||
const STATE_KEY_RIGHT = 'stateRight';
|
||||
|
||||
export class Wrapper extends Component<WrapperProps, WrapperState> {
|
||||
urlStates: { [key: string]: string };
|
||||
export class Wrapper extends Component<WrapperProps> {
|
||||
// urlStates: { [key: string]: string };
|
||||
|
||||
constructor(props: WrapperProps) {
|
||||
super(props);
|
||||
this.urlStates = props.urlStates;
|
||||
this.state = {
|
||||
split: Boolean(props.urlStates[STATE_KEY_RIGHT]),
|
||||
splitState: undefined,
|
||||
};
|
||||
// this.urlStates = props.urlStates;
|
||||
}
|
||||
|
||||
onChangeSplit = (split: boolean, splitState: ExploreState) => {
|
||||
this.setState({ split, splitState });
|
||||
// When closing split, remove URL state for split part
|
||||
if (!split) {
|
||||
delete this.urlStates[STATE_KEY_RIGHT];
|
||||
this.props.updateLocation({
|
||||
query: this.urlStates,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSaveState = (key: string, state: ExploreState) => {
|
||||
const urlState = serializeStateToUrlParam(state, true);
|
||||
this.urlStates[key] = urlState;
|
||||
this.props.updateLocation({
|
||||
query: this.urlStates,
|
||||
});
|
||||
};
|
||||
// onSaveState = (key: string, state: ExploreState) => {
|
||||
// const urlState = serializeStateToUrlParam(state, true);
|
||||
// this.urlStates[key] = urlState;
|
||||
// this.props.updateLocation({
|
||||
// query: this.urlStates,
|
||||
// });
|
||||
// };
|
||||
|
||||
render() {
|
||||
const { datasourceSrv } = this.props;
|
||||
const { split } = this.props;
|
||||
// State overrides for props from first Explore
|
||||
const { split, splitState } = this.state;
|
||||
const urlStateLeft = parseUrlState(this.urlStates[STATE_KEY_LEFT]);
|
||||
const urlStateRight = parseUrlState(this.urlStates[STATE_KEY_RIGHT]);
|
||||
// const urlStateLeft = parseUrlState(this.urlStates[STATE_KEY_LEFT]);
|
||||
// const urlStateRight = parseUrlState(this.urlStates[STATE_KEY_RIGHT]);
|
||||
|
||||
return (
|
||||
<div className="explore-wrapper">
|
||||
<ErrorBoundary>
|
||||
<Explore
|
||||
datasourceSrv={datasourceSrv}
|
||||
onChangeSplit={this.onChangeSplit}
|
||||
onSaveState={this.onSaveState}
|
||||
position="left"
|
||||
split={split}
|
||||
stateKey={STATE_KEY_LEFT}
|
||||
urlState={urlStateLeft}
|
||||
/>
|
||||
<Explore exploreId={ExploreId.left} />
|
||||
</ErrorBoundary>
|
||||
{split && (
|
||||
<ErrorBoundary>
|
||||
<Explore
|
||||
datasourceSrv={datasourceSrv}
|
||||
onChangeSplit={this.onChangeSplit}
|
||||
onSaveState={this.onSaveState}
|
||||
position="right"
|
||||
split={split}
|
||||
splitState={splitState}
|
||||
stateKey={STATE_KEY_RIGHT}
|
||||
urlState={urlStateRight}
|
||||
/>
|
||||
<Explore exploreId={ExploreId.right} />
|
||||
</ErrorBoundary>
|
||||
)}
|
||||
</div>
|
||||
@ -95,9 +55,11 @@ export class Wrapper extends Component<WrapperProps, WrapperState> {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
urlStates: state.location.query,
|
||||
});
|
||||
const mapStateToProps = (state: StoreState) => {
|
||||
// urlStates: state.location.query,
|
||||
const { split } = state.explore;
|
||||
return { split };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateLocation,
|
||||
|
@ -17,6 +17,7 @@ import { DataSourceSelectItem } from 'app/types/datasources';
|
||||
import { DataQuery, StoreState } from 'app/types';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
import {
|
||||
ExploreId,
|
||||
HistoryItem,
|
||||
RangeScanner,
|
||||
ResultType,
|
||||
@ -26,7 +27,7 @@ import {
|
||||
QueryHintGetter,
|
||||
} from 'app/types/explore';
|
||||
import { Emitter } from 'app/core/core';
|
||||
import { dispatch } from 'rxjs/internal/observable/pairs';
|
||||
import { ExploreItemState } from './reducers';
|
||||
|
||||
export enum ActionTypes {
|
||||
AddQueryRow = 'ADD_QUERY_ROW',
|
||||
@ -35,9 +36,11 @@ export enum ActionTypes {
|
||||
ChangeSize = 'CHANGE_SIZE',
|
||||
ChangeTime = 'CHANGE_TIME',
|
||||
ClickClear = 'CLICK_CLEAR',
|
||||
ClickCloseSplit = 'CLICK_CLOSE_SPLIT',
|
||||
ClickExample = 'CLICK_EXAMPLE',
|
||||
ClickGraphButton = 'CLICK_GRAPH_BUTTON',
|
||||
ClickLogsButton = 'CLICK_LOGS_BUTTON',
|
||||
ClickSplit = 'CLICK_SPLIT',
|
||||
ClickTableButton = 'CLICK_TABLE_BUTTON',
|
||||
HighlightLogsExpression = 'HIGHLIGHT_LOGS_EXPRESSION',
|
||||
InitializeExplore = 'INITIALIZE_EXPLORE',
|
||||
@ -59,12 +62,14 @@ export enum ActionTypes {
|
||||
|
||||
export interface AddQueryRowAction {
|
||||
type: ActionTypes.AddQueryRow;
|
||||
exploreId: ExploreId;
|
||||
index: number;
|
||||
query: DataQuery;
|
||||
}
|
||||
|
||||
export interface ChangeQueryAction {
|
||||
type: ActionTypes.ChangeQuery;
|
||||
exploreId: ExploreId;
|
||||
query: DataQuery;
|
||||
index: number;
|
||||
override: boolean;
|
||||
@ -72,38 +77,55 @@ export interface ChangeQueryAction {
|
||||
|
||||
export interface ChangeSizeAction {
|
||||
type: ActionTypes.ChangeSize;
|
||||
exploreId: ExploreId;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export interface ChangeTimeAction {
|
||||
type: ActionTypes.ChangeTime;
|
||||
exploreId: ExploreId;
|
||||
range: TimeRange;
|
||||
}
|
||||
|
||||
export interface ClickClearAction {
|
||||
type: ActionTypes.ClickClear;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ClickCloseSplitAction {
|
||||
type: ActionTypes.ClickCloseSplit;
|
||||
}
|
||||
|
||||
export interface ClickExampleAction {
|
||||
type: ActionTypes.ClickExample;
|
||||
exploreId: ExploreId;
|
||||
query: DataQuery;
|
||||
}
|
||||
|
||||
export interface ClickGraphButtonAction {
|
||||
type: ActionTypes.ClickGraphButton;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ClickLogsButtonAction {
|
||||
type: ActionTypes.ClickLogsButton;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface ClickSplitAction {
|
||||
type: ActionTypes.ClickSplit;
|
||||
itemState: ExploreItemState;
|
||||
}
|
||||
|
||||
export interface ClickTableButtonAction {
|
||||
type: ActionTypes.ClickTableButton;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface InitializeExploreAction {
|
||||
type: ActionTypes.InitializeExplore;
|
||||
exploreId: ExploreId;
|
||||
containerWidth: number;
|
||||
datasource: string;
|
||||
eventBridge: Emitter;
|
||||
@ -114,25 +136,30 @@ export interface InitializeExploreAction {
|
||||
|
||||
export interface HighlightLogsExpressionAction {
|
||||
type: ActionTypes.HighlightLogsExpression;
|
||||
exploreId: ExploreId;
|
||||
expressions: string[];
|
||||
}
|
||||
|
||||
export interface LoadDatasourceFailureAction {
|
||||
type: ActionTypes.LoadDatasourceFailure;
|
||||
exploreId: ExploreId;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface LoadDatasourcePendingAction {
|
||||
type: ActionTypes.LoadDatasourcePending;
|
||||
exploreId: ExploreId;
|
||||
datasourceId: number;
|
||||
}
|
||||
|
||||
export interface LoadDatasourceMissingAction {
|
||||
type: ActionTypes.LoadDatasourceMissing;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export interface LoadDatasourceSuccessAction {
|
||||
type: ActionTypes.LoadDatasourceSuccess;
|
||||
exploreId: ExploreId;
|
||||
StartPage?: any;
|
||||
datasourceInstance: any;
|
||||
history: HistoryItem[];
|
||||
@ -147,6 +174,7 @@ export interface LoadDatasourceSuccessAction {
|
||||
|
||||
export interface ModifyQueriesAction {
|
||||
type: ActionTypes.ModifyQueries;
|
||||
exploreId: ExploreId;
|
||||
modification: any;
|
||||
index: number;
|
||||
modifier: (queries: DataQuery[], modification: any) => DataQuery[];
|
||||
@ -154,11 +182,13 @@ export interface ModifyQueriesAction {
|
||||
|
||||
export interface QueryTransactionFailureAction {
|
||||
type: ActionTypes.QueryTransactionFailure;
|
||||
exploreId: ExploreId;
|
||||
queryTransactions: QueryTransaction[];
|
||||
}
|
||||
|
||||
export interface QueryTransactionStartAction {
|
||||
type: ActionTypes.QueryTransactionStart;
|
||||
exploreId: ExploreId;
|
||||
resultType: ResultType;
|
||||
rowIndex: number;
|
||||
transaction: QueryTransaction;
|
||||
@ -166,27 +196,32 @@ export interface QueryTransactionStartAction {
|
||||
|
||||
export interface QueryTransactionSuccessAction {
|
||||
type: ActionTypes.QueryTransactionSuccess;
|
||||
exploreId: ExploreId;
|
||||
history: HistoryItem[];
|
||||
queryTransactions: QueryTransaction[];
|
||||
}
|
||||
|
||||
export interface RemoveQueryRowAction {
|
||||
type: ActionTypes.RemoveQueryRow;
|
||||
exploreId: ExploreId;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface ScanStartAction {
|
||||
type: ActionTypes.ScanStart;
|
||||
exploreId: ExploreId;
|
||||
scanner: RangeScanner;
|
||||
}
|
||||
|
||||
export interface ScanRangeAction {
|
||||
type: ActionTypes.ScanRange;
|
||||
exploreId: ExploreId;
|
||||
range: RawTimeRange;
|
||||
}
|
||||
|
||||
export interface ScanStopAction {
|
||||
type: ActionTypes.ScanStop;
|
||||
exploreId: ExploreId;
|
||||
}
|
||||
|
||||
export type Action =
|
||||
@ -195,9 +230,11 @@ export type Action =
|
||||
| ChangeSizeAction
|
||||
| ChangeTimeAction
|
||||
| ClickClearAction
|
||||
| ClickCloseSplitAction
|
||||
| ClickExampleAction
|
||||
| ClickGraphButtonAction
|
||||
| ClickLogsButtonAction
|
||||
| ClickSplitAction
|
||||
| ClickTableButtonAction
|
||||
| HighlightLogsExpressionAction
|
||||
| InitializeExploreAction
|
||||
@ -215,94 +252,126 @@ export type Action =
|
||||
| ScanStopAction;
|
||||
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
|
||||
|
||||
export function addQueryRow(index: number): AddQueryRowAction {
|
||||
export function addQueryRow(exploreId: ExploreId, index: number): AddQueryRowAction {
|
||||
const query = generateEmptyQuery(index + 1);
|
||||
return { type: ActionTypes.AddQueryRow, index, query };
|
||||
return { type: ActionTypes.AddQueryRow, exploreId, index, query };
|
||||
}
|
||||
|
||||
export function changeDatasource(datasource: string): ThunkResult<void> {
|
||||
export function changeDatasource(exploreId: ExploreId, datasource: string): ThunkResult<void> {
|
||||
return async dispatch => {
|
||||
const instance = await getDatasourceSrv().get(datasource);
|
||||
dispatch(loadDatasource(instance));
|
||||
dispatch(loadDatasource(exploreId, instance));
|
||||
};
|
||||
}
|
||||
|
||||
export function changeQuery(query: DataQuery, index: number, override: boolean): ThunkResult<void> {
|
||||
export function changeQuery(
|
||||
exploreId: ExploreId,
|
||||
query: DataQuery,
|
||||
index: number,
|
||||
override: boolean
|
||||
): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
// Null query means reset
|
||||
if (query === null) {
|
||||
query = { ...generateEmptyQuery(index) };
|
||||
}
|
||||
|
||||
dispatch({ type: ActionTypes.ChangeQuery, query, index, override });
|
||||
dispatch({ type: ActionTypes.ChangeQuery, exploreId, query, index, override });
|
||||
if (override) {
|
||||
dispatch(runQueries());
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function changeSize({ height, width }: { height: number; width: number }): ChangeSizeAction {
|
||||
return { type: ActionTypes.ChangeSize, height, width };
|
||||
export function changeSize(
|
||||
exploreId: ExploreId,
|
||||
{ height, width }: { height: number; width: number }
|
||||
): ChangeSizeAction {
|
||||
return { type: ActionTypes.ChangeSize, exploreId, height, width };
|
||||
}
|
||||
|
||||
export function changeTime(range: TimeRange): ThunkResult<void> {
|
||||
export function changeTime(exploreId: ExploreId, range: TimeRange): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch({ type: ActionTypes.ChangeTime, range });
|
||||
dispatch(runQueries());
|
||||
dispatch({ type: ActionTypes.ChangeTime, exploreId, range });
|
||||
dispatch(runQueries(exploreId));
|
||||
};
|
||||
}
|
||||
|
||||
export function clickExample(rawQuery: DataQuery): ThunkResult<void> {
|
||||
export function clickClear(exploreId: ExploreId): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
const query = { ...rawQuery, ...generateEmptyQuery() };
|
||||
dispatch({
|
||||
type: ActionTypes.ClickExample,
|
||||
query,
|
||||
});
|
||||
dispatch(runQueries());
|
||||
};
|
||||
}
|
||||
|
||||
export function clickClear(): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch(scanStop());
|
||||
dispatch({ type: ActionTypes.ClickClear });
|
||||
dispatch(scanStop(exploreId));
|
||||
dispatch({ type: ActionTypes.ClickClear, exploreId });
|
||||
// TODO save state
|
||||
};
|
||||
}
|
||||
|
||||
export function clickGraphButton(): ThunkResult<void> {
|
||||
export function clickCloseSplit(): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch({ type: ActionTypes.ClickCloseSplit });
|
||||
// When closing split, remove URL state for split part
|
||||
// TODO save state
|
||||
};
|
||||
}
|
||||
|
||||
export function clickExample(exploreId: ExploreId, rawQuery: DataQuery): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
const query = { ...rawQuery, ...generateEmptyQuery() };
|
||||
dispatch({
|
||||
type: ActionTypes.ClickExample,
|
||||
exploreId,
|
||||
query,
|
||||
});
|
||||
dispatch(runQueries(exploreId));
|
||||
};
|
||||
}
|
||||
|
||||
export function clickGraphButton(exploreId: ExploreId): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ActionTypes.ClickGraphButton });
|
||||
if (getState().explore.showingGraph) {
|
||||
dispatch(runQueries());
|
||||
dispatch({ type: ActionTypes.ClickGraphButton, exploreId });
|
||||
if (getState().explore[exploreId].showingGraph) {
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function clickLogsButton(): ThunkResult<void> {
|
||||
export function clickLogsButton(exploreId: ExploreId): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ActionTypes.ClickLogsButton });
|
||||
if (getState().explore.showingLogs) {
|
||||
dispatch(runQueries());
|
||||
dispatch({ type: ActionTypes.ClickLogsButton, exploreId });
|
||||
if (getState().explore[exploreId].showingLogs) {
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function clickTableButton(): ThunkResult<void> {
|
||||
export function clickSplit(): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ActionTypes.ClickTableButton });
|
||||
if (getState().explore.showingTable) {
|
||||
dispatch(runQueries());
|
||||
// Clone left state to become the right state
|
||||
const leftState = getState().explore.left;
|
||||
const itemState = {
|
||||
...leftState,
|
||||
queryTransactions: [],
|
||||
initialQueries: leftState.modifiedQueries.slice(),
|
||||
};
|
||||
dispatch({ type: ActionTypes.ClickSplit, itemState });
|
||||
// TODO save state
|
||||
};
|
||||
}
|
||||
|
||||
export function clickTableButton(exploreId: ExploreId): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ActionTypes.ClickTableButton, exploreId });
|
||||
if (getState().explore[exploreId].showingTable) {
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function highlightLogsExpression(expressions: string[]): HighlightLogsExpressionAction {
|
||||
return { type: ActionTypes.HighlightLogsExpression, expressions };
|
||||
export function highlightLogsExpression(exploreId: ExploreId, expressions: string[]): HighlightLogsExpressionAction {
|
||||
return { type: ActionTypes.HighlightLogsExpression, exploreId, expressions };
|
||||
}
|
||||
|
||||
export function initializeExplore(
|
||||
exploreId: ExploreId,
|
||||
datasource: string,
|
||||
queries: DataQuery[],
|
||||
range: RawTimeRange,
|
||||
@ -320,6 +389,7 @@ export function initializeExplore(
|
||||
|
||||
dispatch({
|
||||
type: ActionTypes.InitializeExplore,
|
||||
exploreId,
|
||||
containerWidth,
|
||||
datasource,
|
||||
eventBridge,
|
||||
@ -335,26 +405,35 @@ export function initializeExplore(
|
||||
} else {
|
||||
instance = await getDatasourceSrv().get();
|
||||
}
|
||||
dispatch(loadDatasource(instance));
|
||||
dispatch(loadDatasource(exploreId, instance));
|
||||
} else {
|
||||
dispatch(loadDatasourceMissing);
|
||||
dispatch(loadDatasourceMissing(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const loadDatasourceFailure = (error: string): LoadDatasourceFailureAction => ({
|
||||
export const loadDatasourceFailure = (exploreId: ExploreId, error: string): LoadDatasourceFailureAction => ({
|
||||
type: ActionTypes.LoadDatasourceFailure,
|
||||
exploreId,
|
||||
error,
|
||||
});
|
||||
|
||||
export const loadDatasourceMissing: LoadDatasourceMissingAction = { type: ActionTypes.LoadDatasourceMissing };
|
||||
export const loadDatasourceMissing = (exploreId: ExploreId): LoadDatasourceMissingAction => ({
|
||||
type: ActionTypes.LoadDatasourceMissing,
|
||||
exploreId,
|
||||
});
|
||||
|
||||
export const loadDatasourcePending = (datasourceId: number): LoadDatasourcePendingAction => ({
|
||||
export const loadDatasourcePending = (exploreId: ExploreId, datasourceId: number): LoadDatasourcePendingAction => ({
|
||||
type: ActionTypes.LoadDatasourcePending,
|
||||
exploreId,
|
||||
datasourceId,
|
||||
});
|
||||
|
||||
export const loadDatasourceSuccess = (instance: any, queries: DataQuery[]): LoadDatasourceSuccessAction => {
|
||||
export const loadDatasourceSuccess = (
|
||||
exploreId: ExploreId,
|
||||
instance: any,
|
||||
queries: DataQuery[]
|
||||
): LoadDatasourceSuccessAction => {
|
||||
// Capabilities
|
||||
const supportsGraph = instance.meta.metrics;
|
||||
const supportsLogs = instance.meta.logs;
|
||||
@ -369,6 +448,7 @@ export const loadDatasourceSuccess = (instance: any, queries: DataQuery[]): Load
|
||||
|
||||
return {
|
||||
type: ActionTypes.LoadDatasourceSuccess,
|
||||
exploreId,
|
||||
StartPage,
|
||||
datasourceInstance: instance,
|
||||
history,
|
||||
@ -381,12 +461,12 @@ export const loadDatasourceSuccess = (instance: any, queries: DataQuery[]): Load
|
||||
};
|
||||
};
|
||||
|
||||
export function loadDatasource(instance: any): ThunkResult<void> {
|
||||
export function loadDatasource(exploreId: ExploreId, instance: any): ThunkResult<void> {
|
||||
return async (dispatch, getState) => {
|
||||
const datasourceId = instance.meta.id;
|
||||
|
||||
// Keep ID to track selection
|
||||
dispatch(loadDatasourcePending(datasourceId));
|
||||
dispatch(loadDatasourcePending(exploreId, datasourceId));
|
||||
|
||||
let datasourceError = null;
|
||||
try {
|
||||
@ -396,11 +476,11 @@ export function loadDatasource(instance: any): ThunkResult<void> {
|
||||
datasourceError = (error && error.statusText) || 'Network error';
|
||||
}
|
||||
if (datasourceError) {
|
||||
dispatch(loadDatasourceFailure(datasourceError));
|
||||
dispatch(loadDatasourceFailure(exploreId, datasourceError));
|
||||
return;
|
||||
}
|
||||
|
||||
if (datasourceId !== getState().explore.requestedDatasourceId) {
|
||||
if (datasourceId !== getState().explore[exploreId].requestedDatasourceId) {
|
||||
// User already changed datasource again, discard results
|
||||
return;
|
||||
}
|
||||
@ -410,9 +490,9 @@ export function loadDatasource(instance: any): ThunkResult<void> {
|
||||
}
|
||||
|
||||
// Check if queries can be imported from previously selected datasource
|
||||
const queries = getState().explore.modifiedQueries;
|
||||
const queries = getState().explore[exploreId].modifiedQueries;
|
||||
let importedQueries = queries;
|
||||
const origin = getState().explore.datasourceInstance;
|
||||
const origin = getState().explore[exploreId].datasourceInstance;
|
||||
if (origin) {
|
||||
if (origin.meta.id === instance.meta.id) {
|
||||
// Keep same queries if same type of datasource
|
||||
@ -426,7 +506,7 @@ export function loadDatasource(instance: any): ThunkResult<void> {
|
||||
}
|
||||
}
|
||||
|
||||
if (datasourceId !== getState().explore.requestedDatasourceId) {
|
||||
if (datasourceId !== getState().explore[exploreId].requestedDatasourceId) {
|
||||
// User already changed datasource again, discard results
|
||||
return;
|
||||
}
|
||||
@ -437,23 +517,33 @@ export function loadDatasource(instance: any): ThunkResult<void> {
|
||||
...generateEmptyQuery(i),
|
||||
}));
|
||||
|
||||
dispatch(loadDatasourceSuccess(instance, nextQueries));
|
||||
dispatch(runQueries());
|
||||
dispatch(loadDatasourceSuccess(exploreId, instance, nextQueries));
|
||||
dispatch(runQueries(exploreId));
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyQueries(modification: any, index: number, modifier: any): ThunkResult<void> {
|
||||
export function modifyQueries(
|
||||
exploreId: ExploreId,
|
||||
modification: any,
|
||||
index: number,
|
||||
modifier: any
|
||||
): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch({ type: ActionTypes.ModifyQueries, modification, index, modifier });
|
||||
dispatch({ type: ActionTypes.ModifyQueries, exploreId, modification, index, modifier });
|
||||
if (!modification.preventSubmit) {
|
||||
dispatch(runQueries());
|
||||
dispatch(runQueries(exploreId));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function queryTransactionFailure(transactionId: string, response: any, datasourceId: string): ThunkResult<void> {
|
||||
export function queryTransactionFailure(
|
||||
exploreId: ExploreId,
|
||||
transactionId: string,
|
||||
response: any,
|
||||
datasourceId: string
|
||||
): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const { datasourceInstance, queryTransactions } = getState().explore;
|
||||
const { datasourceInstance, queryTransactions } = getState().explore[exploreId];
|
||||
if (datasourceInstance.meta.id !== datasourceId || response.cancelled) {
|
||||
// Navigated away, queries did not matter
|
||||
return;
|
||||
@ -500,19 +590,21 @@ export function queryTransactionFailure(transactionId: string, response: any, da
|
||||
return qt;
|
||||
});
|
||||
|
||||
dispatch({ type: ActionTypes.QueryTransactionFailure, queryTransactions: nextQueryTransactions });
|
||||
dispatch({ type: ActionTypes.QueryTransactionFailure, exploreId, queryTransactions: nextQueryTransactions });
|
||||
};
|
||||
}
|
||||
|
||||
export function queryTransactionStart(
|
||||
exploreId: ExploreId,
|
||||
transaction: QueryTransaction,
|
||||
resultType: ResultType,
|
||||
rowIndex: number
|
||||
): QueryTransactionStartAction {
|
||||
return { type: ActionTypes.QueryTransactionStart, resultType, rowIndex, transaction };
|
||||
return { type: ActionTypes.QueryTransactionStart, exploreId, resultType, rowIndex, transaction };
|
||||
}
|
||||
|
||||
export function queryTransactionSuccess(
|
||||
exploreId: ExploreId,
|
||||
transactionId: string,
|
||||
result: any,
|
||||
latency: number,
|
||||
@ -520,7 +612,7 @@ export function queryTransactionSuccess(
|
||||
datasourceId: string
|
||||
): ThunkResult<void> {
|
||||
return (dispatch, getState) => {
|
||||
const { datasourceInstance, history, queryTransactions, scanner, scanning } = getState().explore;
|
||||
const { datasourceInstance, history, queryTransactions, scanner, scanning } = getState().explore[exploreId];
|
||||
|
||||
// If datasource already changed, results do not matter
|
||||
if (datasourceInstance.meta.id !== datasourceId) {
|
||||
@ -558,6 +650,7 @@ export function queryTransactionSuccess(
|
||||
|
||||
dispatch({
|
||||
type: ActionTypes.QueryTransactionSuccess,
|
||||
exploreId,
|
||||
history: nextHistory,
|
||||
queryTransactions: nextQueryTransactions,
|
||||
});
|
||||
@ -568,24 +661,24 @@ export function queryTransactionSuccess(
|
||||
const other = nextQueryTransactions.find(qt => qt.scanning && !qt.done);
|
||||
if (!other) {
|
||||
const range = scanner();
|
||||
dispatch({ type: ActionTypes.ScanRange, range });
|
||||
dispatch({ type: ActionTypes.ScanRange, exploreId, range });
|
||||
}
|
||||
} else {
|
||||
// We can stop scanning if we have a result
|
||||
dispatch(scanStop());
|
||||
dispatch(scanStop(exploreId));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function removeQueryRow(index: number): ThunkResult<void> {
|
||||
export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch({ type: ActionTypes.RemoveQueryRow, index });
|
||||
dispatch(runQueries());
|
||||
dispatch({ type: ActionTypes.RemoveQueryRow, exploreId, index });
|
||||
dispatch(runQueries(exploreId));
|
||||
};
|
||||
}
|
||||
|
||||
export function runQueries() {
|
||||
export function runQueries(exploreId: ExploreId) {
|
||||
return (dispatch, getState) => {
|
||||
const {
|
||||
datasourceInstance,
|
||||
@ -596,10 +689,10 @@ export function runQueries() {
|
||||
supportsGraph,
|
||||
supportsLogs,
|
||||
supportsTable,
|
||||
} = getState().explore;
|
||||
} = getState().explore[exploreId];
|
||||
|
||||
if (!hasNonEmptyQuery(modifiedQueries)) {
|
||||
dispatch({ type: ActionTypes.RunQueriesEmpty });
|
||||
dispatch({ type: ActionTypes.RunQueriesEmpty, exploreId });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -611,6 +704,7 @@ export function runQueries() {
|
||||
if (showingTable && supportsTable) {
|
||||
dispatch(
|
||||
runQueriesForType(
|
||||
exploreId,
|
||||
'Table',
|
||||
{
|
||||
interval,
|
||||
@ -625,6 +719,7 @@ export function runQueries() {
|
||||
if (showingGraph && supportsGraph) {
|
||||
dispatch(
|
||||
runQueriesForType(
|
||||
exploreId,
|
||||
'Graph',
|
||||
{
|
||||
interval,
|
||||
@ -636,13 +731,18 @@ export function runQueries() {
|
||||
);
|
||||
}
|
||||
if (showingLogs && supportsLogs) {
|
||||
dispatch(runQueriesForType('Logs', { interval, format: 'logs' }));
|
||||
dispatch(runQueriesForType(exploreId, 'Logs', { interval, format: 'logs' }));
|
||||
}
|
||||
// TODO save state
|
||||
};
|
||||
}
|
||||
|
||||
function runQueriesForType(resultType: ResultType, queryOptions: QueryOptions, resultGetter?: any) {
|
||||
function runQueriesForType(
|
||||
exploreId: ExploreId,
|
||||
resultType: ResultType,
|
||||
queryOptions: QueryOptions,
|
||||
resultGetter?: any
|
||||
) {
|
||||
return async (dispatch, getState) => {
|
||||
const {
|
||||
datasourceInstance,
|
||||
@ -651,7 +751,7 @@ function runQueriesForType(resultType: ResultType, queryOptions: QueryOptions, r
|
||||
queryIntervals,
|
||||
range,
|
||||
scanning,
|
||||
} = getState().explore;
|
||||
} = getState().explore[exploreId];
|
||||
const datasourceId = datasourceInstance.meta.id;
|
||||
|
||||
// Run all queries concurrently
|
||||
@ -665,30 +765,30 @@ function runQueriesForType(resultType: ResultType, queryOptions: QueryOptions, r
|
||||
queryIntervals,
|
||||
scanning
|
||||
);
|
||||
dispatch(queryTransactionStart(transaction, resultType, rowIndex));
|
||||
dispatch(queryTransactionStart(exploreId, transaction, resultType, rowIndex));
|
||||
try {
|
||||
const now = Date.now();
|
||||
const res = await datasourceInstance.query(transaction.options);
|
||||
eventBridge.emit('data-received', res.data || []);
|
||||
const latency = Date.now() - now;
|
||||
const results = resultGetter ? resultGetter(res.data) : res.data;
|
||||
dispatch(queryTransactionSuccess(transaction.id, results, latency, queries, datasourceId));
|
||||
dispatch(queryTransactionSuccess(exploreId, transaction.id, results, latency, queries, datasourceId));
|
||||
} catch (response) {
|
||||
eventBridge.emit('data-error', response);
|
||||
dispatch(queryTransactionFailure(transaction.id, response, datasourceId));
|
||||
dispatch(queryTransactionFailure(exploreId, transaction.id, response, datasourceId));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function scanStart(scanner: RangeScanner): ThunkResult<void> {
|
||||
export function scanStart(exploreId: ExploreId, scanner: RangeScanner): ThunkResult<void> {
|
||||
return dispatch => {
|
||||
dispatch({ type: ActionTypes.ScanStart, scanner });
|
||||
dispatch({ type: ActionTypes.ScanStart, exploreId, scanner });
|
||||
const range = scanner();
|
||||
dispatch({ type: ActionTypes.ScanRange, range });
|
||||
dispatch({ type: ActionTypes.ScanRange, exploreId, range });
|
||||
};
|
||||
}
|
||||
|
||||
export function scanStop(): ScanStopAction {
|
||||
return { type: ActionTypes.ScanStop };
|
||||
export function scanStop(exploreId: ExploreId): ScanStopAction {
|
||||
return { type: ActionTypes.ScanStop, exploreId };
|
||||
}
|
||||
|
@ -16,7 +16,14 @@ import { LogsModel } from 'app/core/logs_model';
|
||||
import TableModel from 'app/core/table_model';
|
||||
|
||||
// TODO move to types
|
||||
|
||||
export interface ExploreState {
|
||||
split: boolean;
|
||||
left: ExploreItemState;
|
||||
right: ExploreItemState;
|
||||
}
|
||||
|
||||
export interface ExploreItemState {
|
||||
StartPage?: any;
|
||||
containerWidth: number;
|
||||
datasourceInstance: any;
|
||||
@ -57,7 +64,7 @@ export const DEFAULT_RANGE = {
|
||||
// Millies step for helper bar charts
|
||||
const DEFAULT_GRAPH_INTERVAL = 15 * 1000;
|
||||
|
||||
const initialExploreState: ExploreState = {
|
||||
const makeExploreItemState = (): ExploreItemState => ({
|
||||
StartPage: undefined,
|
||||
containerWidth: 0,
|
||||
datasourceInstance: null,
|
||||
@ -79,9 +86,15 @@ const initialExploreState: ExploreState = {
|
||||
supportsGraph: null,
|
||||
supportsLogs: null,
|
||||
supportsTable: null,
|
||||
});
|
||||
|
||||
const initialExploreState: ExploreState = {
|
||||
split: false,
|
||||
left: makeExploreItemState(),
|
||||
right: makeExploreItemState(),
|
||||
};
|
||||
|
||||
export const exploreReducer = (state = initialExploreState, action: Action): ExploreState => {
|
||||
const itemReducer = (state, action: Action): ExploreItemState => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.AddQueryRow: {
|
||||
const { initialQueries, modifiedQueries, queryTransactions } = state;
|
||||
@ -407,6 +420,36 @@ export const exploreReducer = (state = initialExploreState, action: Action): Exp
|
||||
return state;
|
||||
};
|
||||
|
||||
export const exploreReducer = (state = initialExploreState, action: Action): ExploreState => {
|
||||
switch (action.type) {
|
||||
case ActionTypes.ClickCloseSplit: {
|
||||
return {
|
||||
...state,
|
||||
split: false,
|
||||
};
|
||||
}
|
||||
|
||||
case ActionTypes.ClickSplit: {
|
||||
return {
|
||||
...state,
|
||||
split: true,
|
||||
right: action.itemState,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const { exploreId } = action as any;
|
||||
if (exploreId !== undefined) {
|
||||
const exploreItemState = state[exploreId];
|
||||
return {
|
||||
...state,
|
||||
[exploreId]: itemReducer(exploreItemState, action),
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default {
|
||||
explore: exploreReducer,
|
||||
};
|
||||
|
@ -75,6 +75,11 @@ export interface CompletionItemGroup {
|
||||
skipSort?: boolean;
|
||||
}
|
||||
|
||||
export enum ExploreId {
|
||||
left = 'left',
|
||||
right = 'right',
|
||||
}
|
||||
|
||||
export interface HistoryItem {
|
||||
ts: number;
|
||||
query: DataQuery;
|
||||
|
@ -1,5 +1,5 @@
|
||||
.explore {
|
||||
width: 100%;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&-container {
|
||||
padding: $dashboard-padding;
|
||||
|
Loading…
Reference in New Issue
Block a user