From 6ff15039a9bcb6fa79f98ad8fb0224f633aa4936 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Tue, 15 Jan 2019 19:52:53 +0100 Subject: [PATCH] File organization, action naming, comments - moved ActionTypes to `./state/actionTypes` - renamed click-related actions - added comments to actions and state types - prefixed Explore actions with `explore/` - fixed query override issue when row was added --- public/app/features/explore/Explore.tsx | 34 +- .../app/features/explore/GraphContainer.tsx | 8 +- public/app/features/explore/LogsContainer.tsx | 8 +- .../app/features/explore/TableContainer.tsx | 14 +- .../app/features/explore/state/actionTypes.ts | 252 +++++++++ public/app/features/explore/state/actions.ts | 498 +++++++----------- public/app/features/explore/state/reducers.ts | 210 ++++---- public/app/types/explore.ts | 117 ++++ 8 files changed, 714 insertions(+), 427 deletions(-) create mode 100644 public/app/features/explore/state/actionTypes.ts diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index a70135c00ad..a8acab50137 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -18,15 +18,15 @@ import { changeDatasource, changeSize, changeTime, - clickClear, - clickCloseSplit, - clickExample, - clickSplit, + clearQueries, initializeExplore, modifyQueries, runQueries, scanStart, scanStop, + setQueries, + splitClose, + splitOpen, } from './state/actions'; import { Alert } from './Error'; @@ -42,10 +42,7 @@ interface ExploreProps { changeDatasource: typeof changeDatasource; changeSize: typeof changeSize; changeTime: typeof changeTime; - clickClear: typeof clickClear; - clickCloseSplit: typeof clickCloseSplit; - clickExample: typeof clickExample; - clickSplit: typeof clickSplit; + clearQueries: typeof clearQueries; datasourceError: string; datasourceInstance: any; datasourceLoading: boolean | null; @@ -65,7 +62,10 @@ interface ExploreProps { scanRange?: RawTimeRange; scanStart: typeof scanStart; scanStop: typeof scanStop; + setQueries: typeof setQueries; split: boolean; + splitClose: typeof splitClose; + splitOpen: typeof splitOpen; showingStartPage?: boolean; supportsGraph: boolean | null; supportsLogs: boolean | null; @@ -152,20 +152,20 @@ export class Explore extends React.PureComponent { }; onClickClear = () => { - this.props.clickClear(this.props.exploreId); + this.props.clearQueries(this.props.exploreId); }; onClickCloseSplit = () => { - this.props.clickCloseSplit(); + this.props.splitClose(); }; // Use this in help pages to set page to a single query onClickExample = (query: DataQuery) => { - this.props.clickExample(this.props.exploreId, query); + this.props.setQueries(this.props.exploreId, [query]); }; onClickSplit = () => { - this.props.clickSplit(); + this.props.splitOpen(); }; onClickLabel = (key: string, value: string) => { @@ -175,7 +175,7 @@ export class Explore extends React.PureComponent { onModifyQueries = (action, index?: number) => { const { datasourceInstance } = this.props; if (datasourceInstance && datasourceInstance.modifyQuery) { - const modifier = (queries: DataQuery, action: any) => datasourceInstance.modifyQuery(queries, action); + const modifier = (queries: DataQuery, modification: any) => datasourceInstance.modifyQuery(queries, modification); this.props.modifyQueries(this.props.exploreId, action, index, modifier); } }; @@ -366,15 +366,15 @@ const mapDispatchToProps = { changeDatasource, changeSize, changeTime, - clickClear, - clickCloseSplit, - clickExample, - clickSplit, + clearQueries, initializeExplore, modifyQueries, runQueries, scanStart, scanStop, + setQueries, + splitClose, + splitOpen, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(Explore)); diff --git a/public/app/features/explore/GraphContainer.tsx b/public/app/features/explore/GraphContainer.tsx index da098f0c92d..e2610bcc781 100644 --- a/public/app/features/explore/GraphContainer.tsx +++ b/public/app/features/explore/GraphContainer.tsx @@ -6,13 +6,12 @@ import { RawTimeRange, TimeRange } from '@grafana/ui'; import { ExploreId, ExploreItemState } from 'app/types/explore'; import { StoreState } from 'app/types'; -import { clickGraphButton } from './state/actions'; +import { toggleGraph } from './state/actions'; import Graph from './Graph'; import Panel from './Panel'; interface GraphContainerProps { onChangeTime: (range: TimeRange) => void; - clickGraphButton: typeof clickGraphButton; exploreId: ExploreId; graphResult?: any[]; loading: boolean; @@ -20,11 +19,12 @@ interface GraphContainerProps { showingGraph: boolean; showingTable: boolean; split: boolean; + toggleGraph: typeof toggleGraph; } export class GraphContainer extends PureComponent { onClickGraphButton = () => { - this.props.clickGraphButton(this.props.exploreId); + this.props.toggleGraph(this.props.exploreId); }; render() { @@ -55,7 +55,7 @@ function mapStateToProps(state: StoreState, { exploreId }) { } const mapDispatchToProps = { - clickGraphButton, + toggleGraph, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(GraphContainer)); diff --git a/public/app/features/explore/LogsContainer.tsx b/public/app/features/explore/LogsContainer.tsx index db2f681a9c5..e58cd2b5e95 100644 --- a/public/app/features/explore/LogsContainer.tsx +++ b/public/app/features/explore/LogsContainer.tsx @@ -7,12 +7,11 @@ import { ExploreId, ExploreItemState } from 'app/types/explore'; import { LogsModel } from 'app/core/logs_model'; import { StoreState } from 'app/types'; -import { clickLogsButton } from './state/actions'; +import { toggleLogs } from './state/actions'; import Logs from './Logs'; import Panel from './Panel'; interface LogsContainerProps { - clickLogsButton: typeof clickLogsButton; exploreId: ExploreId; loading: boolean; logsHighlighterExpressions?: string[]; @@ -25,11 +24,12 @@ interface LogsContainerProps { scanning?: boolean; scanRange?: RawTimeRange; showingLogs: boolean; + toggleLogs: typeof toggleLogs; } export class LogsContainer extends PureComponent { onClickLogsButton = () => { - this.props.clickLogsButton(this.props.exploreId); + this.props.toggleLogs(this.props.exploreId); }; render() { @@ -85,7 +85,7 @@ function mapStateToProps(state: StoreState, { exploreId }) { } const mapDispatchToProps = { - clickLogsButton, + toggleLogs, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(LogsContainer)); diff --git a/public/app/features/explore/TableContainer.tsx b/public/app/features/explore/TableContainer.tsx index e510a77c97f..1d00a441e14 100644 --- a/public/app/features/explore/TableContainer.tsx +++ b/public/app/features/explore/TableContainer.tsx @@ -5,30 +5,30 @@ import { connect } from 'react-redux'; import { ExploreId, ExploreItemState } from 'app/types/explore'; import { StoreState } from 'app/types'; -import { clickTableButton } from './state/actions'; +import { toggleGraph } from './state/actions'; import Table from './Table'; import Panel from './Panel'; import TableModel from 'app/core/table_model'; interface TableContainerProps { - clickTableButton: typeof clickTableButton; exploreId: ExploreId; loading: boolean; - onClickLabel: (key: string, value: string) => void; + onClickCell: (key: string, value: string) => void; showingTable: boolean; tableResult?: TableModel; + toggleGraph: typeof toggleGraph; } export class TableContainer extends PureComponent { onClickTableButton = () => { - this.props.clickTableButton(this.props.exploreId); + this.props.toggleGraph(this.props.exploreId); }; render() { - const { loading, onClickLabel, showingTable, tableResult } = this.props; + const { loading, onClickCell, showingTable, tableResult } = this.props; return ( - +
); } @@ -43,7 +43,7 @@ function mapStateToProps(state: StoreState, { exploreId }) { } const mapDispatchToProps = { - clickTableButton, + toggleGraph, }; export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(TableContainer)); diff --git a/public/app/features/explore/state/actionTypes.ts b/public/app/features/explore/state/actionTypes.ts new file mode 100644 index 00000000000..ed0995cff17 --- /dev/null +++ b/public/app/features/explore/state/actionTypes.ts @@ -0,0 +1,252 @@ +import { RawTimeRange, TimeRange } from '@grafana/ui'; + +import { Emitter } from 'app/core/core'; +import { + ExploreId, + ExploreItemState, + HistoryItem, + RangeScanner, + ResultType, + QueryTransaction, +} from 'app/types/explore'; +import { DataSourceSelectItem } from 'app/types/datasources'; +import { DataQuery } from 'app/types'; + +export enum ActionTypes { + AddQueryRow = 'explore/ADD_QUERY_ROW', + ChangeDatasource = 'explore/CHANGE_DATASOURCE', + ChangeQuery = 'explore/CHANGE_QUERY', + ChangeSize = 'explore/CHANGE_SIZE', + ChangeTime = 'explore/CHANGE_TIME', + ClearQueries = 'explore/CLEAR_QUERIES', + HighlightLogsExpression = 'explore/HIGHLIGHT_LOGS_EXPRESSION', + InitializeExplore = 'explore/INITIALIZE_EXPLORE', + InitializeExploreSplit = 'explore/INITIALIZE_EXPLORE_SPLIT', + LoadDatasourceFailure = 'explore/LOAD_DATASOURCE_FAILURE', + LoadDatasourceMissing = 'explore/LOAD_DATASOURCE_MISSING', + LoadDatasourcePending = 'explore/LOAD_DATASOURCE_PENDING', + LoadDatasourceSuccess = 'explore/LOAD_DATASOURCE_SUCCESS', + ModifyQueries = 'explore/MODIFY_QUERIES', + QueryTransactionFailure = 'explore/QUERY_TRANSACTION_FAILURE', + QueryTransactionStart = 'explore/QUERY_TRANSACTION_START', + QueryTransactionSuccess = 'explore/QUERY_TRANSACTION_SUCCESS', + RemoveQueryRow = 'explore/REMOVE_QUERY_ROW', + RunQueries = 'explore/RUN_QUERIES', + RunQueriesEmpty = 'explore/RUN_QUERIES_EMPTY', + ScanRange = 'explore/SCAN_RANGE', + ScanStart = 'explore/SCAN_START', + ScanStop = 'explore/SCAN_STOP', + SetQueries = 'explore/SET_QUERIES', + SplitClose = 'explore/SPLIT_CLOSE', + SplitOpen = 'explore/SPLIT_OPEN', + StateSave = 'explore/STATE_SAVE', + ToggleGraph = 'explore/TOGGLE_GRAPH', + ToggleLogs = 'explore/TOGGLE_LOGS', + ToggleTable = 'explore/TOGGLE_TABLE', +} + +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; +} + +export interface ChangeSizeAction { + type: ActionTypes.ChangeSize; + exploreId: ExploreId; + width: number; + height: number; +} + +export interface ChangeTimeAction { + type: ActionTypes.ChangeTime; + exploreId: ExploreId; + range: TimeRange; +} + +export interface ClearQueriesAction { + type: ActionTypes.ClearQueries; + exploreId: ExploreId; +} + +export interface HighlightLogsExpressionAction { + type: ActionTypes.HighlightLogsExpression; + exploreId: ExploreId; + expressions: string[]; +} + +export interface InitializeExploreAction { + type: ActionTypes.InitializeExplore; + exploreId: ExploreId; + containerWidth: number; + datasource: string; + eventBridge: Emitter; + exploreDatasources: DataSourceSelectItem[]; + queries: DataQuery[]; + range: RawTimeRange; +} + +export interface InitializeExploreSplitAction { + type: ActionTypes.InitializeExploreSplit; +} + +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[]; + initialDatasource: string; + initialQueries: DataQuery[]; + logsHighlighterExpressions?: any[]; + showingStartPage: boolean; + supportsGraph: boolean; + supportsLogs: boolean; + supportsTable: boolean; +} + +export interface ModifyQueriesAction { + type: ActionTypes.ModifyQueries; + exploreId: ExploreId; + modification: any; + index: number; + modifier: (queries: DataQuery[], modification: any) => DataQuery[]; +} + +export interface QueryTransactionFailureAction { + type: ActionTypes.QueryTransactionFailure; + exploreId: ExploreId; + queryTransactions: QueryTransaction[]; +} + +export interface QueryTransactionStartAction { + type: ActionTypes.QueryTransactionStart; + exploreId: ExploreId; + resultType: ResultType; + rowIndex: number; + transaction: QueryTransaction; +} + +export interface QueryTransactionSuccessAction { + type: ActionTypes.QueryTransactionSuccess; + exploreId: ExploreId; + history: HistoryItem[]; + queryTransactions: QueryTransaction[]; +} + +export interface RemoveQueryRowAction { + type: ActionTypes.RemoveQueryRow; + exploreId: ExploreId; + index: number; +} + +export interface RunQueriesEmptyAction { + type: ActionTypes.RunQueriesEmpty; + exploreId: ExploreId; +} + +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 interface SetQueriesAction { + type: ActionTypes.SetQueries; + exploreId: ExploreId; + queries: DataQuery[]; +} + +export interface SplitCloseAction { + type: ActionTypes.SplitClose; +} + +export interface SplitOpenAction { + type: ActionTypes.SplitOpen; + itemState: ExploreItemState; +} + +export interface StateSaveAction { + type: ActionTypes.StateSave; +} + +export interface ToggleTableAction { + type: ActionTypes.ToggleTable; + exploreId: ExploreId; +} + +export interface ToggleGraphAction { + type: ActionTypes.ToggleGraph; + exploreId: ExploreId; +} + +export interface ToggleLogsAction { + type: ActionTypes.ToggleLogs; + exploreId: ExploreId; +} + +export type Action = + | AddQueryRowAction + | ChangeQueryAction + | ChangeSizeAction + | ChangeTimeAction + | ClearQueriesAction + | HighlightLogsExpressionAction + | InitializeExploreAction + | InitializeExploreSplitAction + | LoadDatasourceFailureAction + | LoadDatasourceMissingAction + | LoadDatasourcePendingAction + | LoadDatasourceSuccessAction + | ModifyQueriesAction + | QueryTransactionFailureAction + | QueryTransactionStartAction + | QueryTransactionSuccessAction + | RemoveQueryRowAction + | RunQueriesEmptyAction + | ScanRangeAction + | ScanStartAction + | ScanStopAction + | SetQueriesAction + | SplitCloseAction + | SplitOpenAction + | ToggleGraphAction + | ToggleLogsAction + | ToggleTableAction; diff --git a/public/app/features/explore/state/actions.ts b/public/app/features/explore/state/actions.ts index 26811606bd9..ecfb35c8c2f 100644 --- a/public/app/features/explore/state/actions.ts +++ b/public/app/features/explore/state/actions.ts @@ -21,9 +21,7 @@ import { DataQuery, StoreState } from 'app/types'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { ExploreId, - ExploreItemState, ExploreUrlState, - HistoryItem, RangeScanner, ResultType, QueryOptions, @@ -33,245 +31,33 @@ import { } from 'app/types/explore'; import { Emitter } from 'app/core/core'; -export enum ActionTypes { - AddQueryRow = 'ADD_QUERY_ROW', - ChangeDatasource = 'CHANGE_DATASOURCE', - ChangeQuery = 'CHANGE_QUERY', - 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', - InitializeExploreSplit = 'INITIALIZE_EXPLORE_SPLIT', - LoadDatasourceFailure = 'LOAD_DATASOURCE_FAILURE', - LoadDatasourceMissing = 'LOAD_DATASOURCE_MISSING', - LoadDatasourcePending = 'LOAD_DATASOURCE_PENDING', - LoadDatasourceSuccess = 'LOAD_DATASOURCE_SUCCESS', - ModifyQueries = 'MODIFY_QUERIES', - QueryTransactionFailure = 'QUERY_TRANSACTION_FAILURE', - QueryTransactionStart = 'QUERY_TRANSACTION_START', - QueryTransactionSuccess = 'QUERY_TRANSACTION_SUCCESS', - RemoveQueryRow = 'REMOVE_QUERY_ROW', - RunQueries = 'RUN_QUERIES', - RunQueriesEmpty = 'RUN_QUERIES', - ScanRange = 'SCAN_RANGE', - ScanStart = 'SCAN_START', - ScanStop = 'SCAN_STOP', - StateSave = 'STATE_SAVE', -} +import { + Action as ThunkableAction, + ActionTypes, + AddQueryRowAction, + ChangeSizeAction, + HighlightLogsExpressionAction, + LoadDatasourceFailureAction, + LoadDatasourceMissingAction, + LoadDatasourcePendingAction, + LoadDatasourceSuccessAction, + QueryTransactionStartAction, + ScanStopAction, +} from './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; -} - -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 HighlightLogsExpressionAction { - type: ActionTypes.HighlightLogsExpression; - exploreId: ExploreId; - expressions: string[]; -} - -export interface InitializeExploreAction { - type: ActionTypes.InitializeExplore; - exploreId: ExploreId; - containerWidth: number; - datasource: string; - eventBridge: Emitter; - exploreDatasources: DataSourceSelectItem[]; - queries: DataQuery[]; - range: RawTimeRange; -} - -export interface InitializeExploreSplitAction { - type: ActionTypes.InitializeExploreSplit; -} - -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[]; - initialDatasource: string; - initialQueries: DataQuery[]; - logsHighlighterExpressions?: any[]; - showingStartPage: boolean; - supportsGraph: boolean; - supportsLogs: boolean; - supportsTable: boolean; -} - -export interface ModifyQueriesAction { - type: ActionTypes.ModifyQueries; - exploreId: ExploreId; - modification: any; - index: number; - modifier: (queries: DataQuery[], modification: any) => DataQuery[]; -} - -export interface QueryTransactionFailureAction { - type: ActionTypes.QueryTransactionFailure; - exploreId: ExploreId; - queryTransactions: QueryTransaction[]; -} - -export interface QueryTransactionStartAction { - type: ActionTypes.QueryTransactionStart; - exploreId: ExploreId; - resultType: ResultType; - rowIndex: number; - transaction: QueryTransaction; -} - -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 interface StateSaveAction { - type: ActionTypes.StateSave; -} - -export type Action = - | AddQueryRowAction - | ChangeQueryAction - | ChangeSizeAction - | ChangeTimeAction - | ClickClearAction - | ClickCloseSplitAction - | ClickExampleAction - | ClickGraphButtonAction - | ClickLogsButtonAction - | ClickSplitAction - | ClickTableButtonAction - | HighlightLogsExpressionAction - | InitializeExploreAction - | InitializeExploreSplitAction - | LoadDatasourceFailureAction - | LoadDatasourceMissingAction - | LoadDatasourcePendingAction - | LoadDatasourceSuccessAction - | ModifyQueriesAction - | QueryTransactionFailureAction - | QueryTransactionStartAction - | QueryTransactionSuccessAction - | RemoveQueryRowAction - | ScanRangeAction - | ScanStartAction - | ScanStopAction; -type ThunkResult = ThunkAction; +type ThunkResult = ThunkAction; +/** + * Adds a query row after the row with the given index. + */ export function addQueryRow(exploreId: ExploreId, index: number): AddQueryRowAction { const query = generateEmptyQuery(index + 1); return { type: ActionTypes.AddQueryRow, exploreId, index, query }; } +/** + * Loads a new datasource identified by the given name. + */ export function changeDatasource(exploreId: ExploreId, datasource: string): ThunkResult { return async dispatch => { const instance = await getDatasourceSrv().get(datasource); @@ -279,6 +65,10 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun }; } +/** + * Query change handler for the query row with the given index. + * If `override` is reset the query modifications and run the queries. Use this to set queries via a link. + */ export function changeQuery( exploreId: ExploreId, query: DataQuery, @@ -298,6 +88,10 @@ export function changeQuery( }; } +/** + * Keep track of the Explore container size, in particular the width. + * The width will be used to calculate graph intervals (number of datapoints). + */ export function changeSize( exploreId: ExploreId, { height, width }: { height: number; width: number } @@ -305,6 +99,9 @@ export function changeSize( return { type: ActionTypes.ChangeSize, exploreId, height, width }; } +/** + * Change the time range of Explore. Usually called from the Timepicker or a graph interaction. + */ export function changeTime(exploreId: ExploreId, range: TimeRange): ThunkResult { return dispatch => { dispatch({ type: ActionTypes.ChangeTime, exploreId, range }); @@ -312,78 +109,28 @@ export function changeTime(exploreId: ExploreId, range: TimeRange): ThunkResult< }; } -export function clickClear(exploreId: ExploreId): ThunkResult { +/** + * Clear all queries and results. + */ +export function clearQueries(exploreId: ExploreId): ThunkResult { return dispatch => { dispatch(scanStop(exploreId)); - dispatch({ type: ActionTypes.ClickClear, exploreId }); + dispatch({ type: ActionTypes.ClearQueries, exploreId }); dispatch(stateSave()); }; } -export function clickCloseSplit(): ThunkResult { - return dispatch => { - dispatch({ type: ActionTypes.ClickCloseSplit }); - dispatch(stateSave()); - }; -} - -export function clickExample(exploreId: ExploreId, rawQuery: DataQuery): ThunkResult { - return dispatch => { - const query = { ...rawQuery, ...generateEmptyQuery() }; - dispatch({ - type: ActionTypes.ClickExample, - exploreId, - query, - }); - dispatch(runQueries(exploreId)); - }; -} - -export function clickGraphButton(exploreId: ExploreId): ThunkResult { - return (dispatch, getState) => { - dispatch({ type: ActionTypes.ClickGraphButton, exploreId }); - if (getState().explore[exploreId].showingGraph) { - dispatch(runQueries(exploreId)); - } - }; -} - -export function clickLogsButton(exploreId: ExploreId): ThunkResult { - return (dispatch, getState) => { - dispatch({ type: ActionTypes.ClickLogsButton, exploreId }); - if (getState().explore[exploreId].showingLogs) { - dispatch(runQueries(exploreId)); - } - }; -} - -export function clickSplit(): ThunkResult { - return (dispatch, getState) => { - // 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 }); - dispatch(stateSave()); - }; -} - -export function clickTableButton(exploreId: ExploreId): ThunkResult { - return (dispatch, getState) => { - dispatch({ type: ActionTypes.ClickTableButton, exploreId }); - if (getState().explore[exploreId].showingTable) { - dispatch(runQueries(exploreId)); - } - }; -} - +/** + * Highlight expressions in the log results + */ export function highlightLogsExpression(exploreId: ExploreId, expressions: string[]): HighlightLogsExpressionAction { return { type: ActionTypes.HighlightLogsExpression, exploreId, expressions }; } +/** + * Initialize Explore state with state from the URL and the React component. + * Call this only on components for with the Explore state has not been initialized. + */ export function initializeExplore( exploreId: ExploreId, datasource: string, @@ -426,29 +173,46 @@ export function initializeExplore( }; } +/** + * Initialize the wrapper split state + */ export function initializeExploreSplit() { return async dispatch => { dispatch({ type: ActionTypes.InitializeExploreSplit }); }; } +/** + * Display an error that happened during the selection of a datasource + */ export const loadDatasourceFailure = (exploreId: ExploreId, error: string): LoadDatasourceFailureAction => ({ type: ActionTypes.LoadDatasourceFailure, exploreId, error, }); +/** + * Display an error when no datasources have been configured + */ export const loadDatasourceMissing = (exploreId: ExploreId): LoadDatasourceMissingAction => ({ type: ActionTypes.LoadDatasourceMissing, exploreId, }); +/** + * Start the async process of loading a datasource to display a loading indicator + */ export const loadDatasourcePending = (exploreId: ExploreId, datasourceId: number): LoadDatasourcePendingAction => ({ type: ActionTypes.LoadDatasourcePending, exploreId, datasourceId, }); +/** + * Datasource loading was successfully completed. The instance is stored in the state as well in case we need to + * run datasource-specific code. Existing queries are imported to the new datasource if an importer exists, + * e.g., Prometheus -> Loki queries. + */ export const loadDatasourceSuccess = ( exploreId: ExploreId, instance: any, @@ -481,6 +245,9 @@ export const loadDatasourceSuccess = ( }; }; +/** + * Main action to asynchronously load a datasource. Dispatches lots of smaller actions for feedback. + */ export function loadDatasource(exploreId: ExploreId, instance: any): ThunkResult { return async (dispatch, getState) => { const datasourceId = instance.meta.id; @@ -542,6 +309,13 @@ export function loadDatasource(exploreId: ExploreId, instance: any): ThunkResult }; } +/** + * Action to modify a query given a datasource-specific modifier action. + * @param exploreId Explore area + * @param modification Action object with a type, e.g., ADD_FILTER + * @param index Optional query row index. If omitted, the modification is applied to all query rows. + * @param modifier Function that executes the modification, typically `datasourceInstance.modifyQueries`. + */ export function modifyQueries( exploreId: ExploreId, modification: any, @@ -556,6 +330,10 @@ export function modifyQueries( }; } +/** + * Mark a query transaction as failed with an error extracted from the query response. + * The transaction will be marked as `done`. + */ export function queryTransactionFailure( exploreId: ExploreId, transactionId: string, @@ -614,6 +392,13 @@ export function queryTransactionFailure( }; } +/** + * Start a query transaction for the given result type. + * @param exploreId Explore area + * @param transaction Query options and `done` status. + * @param resultType Associate the transaction with a result viewer, e.g., Graph + * @param rowIndex Index is used to associate latency for this transaction with a query row + */ export function queryTransactionStart( exploreId: ExploreId, transaction: QueryTransaction, @@ -623,6 +408,17 @@ export function queryTransactionStart( return { type: ActionTypes.QueryTransactionStart, exploreId, resultType, rowIndex, transaction }; } +/** + * Complete a query transaction, mark the transaction as `done` and store query state in URL. + * If the transaction was started by a scanner, it keeps on scanning for more results. + * Side-effect: the query is stored in localStorage. + * @param exploreId Explore area + * @param transactionId ID + * @param result Response from `datasourceInstance.query()` + * @param latency Duration between request and response + * @param queries Queries from all query rows + * @param datasourceId Origin datasource instance, used to discard results if current datasource is different + */ export function queryTransactionSuccess( exploreId: ExploreId, transactionId: string, @@ -691,6 +487,9 @@ export function queryTransactionSuccess( }; } +/** + * Remove query row of the given index, as well as associated query results. + */ export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult { return dispatch => { dispatch({ type: ActionTypes.RemoveQueryRow, exploreId, index }); @@ -698,6 +497,9 @@ export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult }; } +/** + * Main action to run queries and dispatches sub-actions based on which result viewers are active + */ export function runQueries(exploreId: ExploreId) { return (dispatch, getState) => { const { @@ -757,6 +559,13 @@ export function runQueries(exploreId: ExploreId) { }; } +/** + * Helper action to build a query transaction object and handing the query to the datasource. + * @param exploreId Explore area + * @param resultType Result viewer that will be associated with this query result + * @param queryOptions Query options as required by the datasource's `query()` function. + * @param resultGetter Optional result extractor, e.g., if the result is a list and you only need the first element. + */ function runQueriesForType( exploreId: ExploreId, resultType: ResultType, @@ -801,18 +610,79 @@ function runQueriesForType( }; } +/** + * Start a scan for more results using the given scanner. + * @param exploreId Explore area + * @param scanner Function that a) returns a new time range and b) triggers a query run for the new range + */ export function scanStart(exploreId: ExploreId, scanner: RangeScanner): ThunkResult { return dispatch => { + // Register the scanner dispatch({ type: ActionTypes.ScanStart, exploreId, scanner }); + // Scanning must trigger query run, and return the new range const range = scanner(); + // Set the new range to be displayed dispatch({ type: ActionTypes.ScanRange, exploreId, range }); }; } +/** + * Stop any scanning for more results. + */ export function scanStop(exploreId: ExploreId): ScanStopAction { return { type: ActionTypes.ScanStop, exploreId }; } +/** + * Reset queries to the given queries. Any modifications will be discarded. + * Use this action for clicks on query examples. Triggers a query run. + */ +export function setQueries(exploreId: ExploreId, rawQueries: DataQuery[]): ThunkResult { + return dispatch => { + // Inject react keys into query objects + const queries = rawQueries.map(q => ({ ...q, ...generateEmptyQuery() })); + dispatch({ + type: ActionTypes.SetQueries, + exploreId, + queries, + }); + dispatch(runQueries(exploreId)); + }; +} + +/** + * Close the split view and save URL state. + */ +export function splitClose(): ThunkResult { + return dispatch => { + dispatch({ type: ActionTypes.SplitClose }); + dispatch(stateSave()); + }; +} + +/** + * Open the split view and copy the left state to be the right state. + * The right state is automatically initialized. + * The copy keeps all query modifications but wipes the query results. + */ +export function splitOpen(): ThunkResult { + return (dispatch, getState) => { + // Clone left state to become the right state + const leftState = getState().explore.left; + const itemState = { + ...leftState, + queryTransactions: [], + initialQueries: leftState.modifiedQueries.slice(), + }; + dispatch({ type: ActionTypes.SplitOpen, itemState }); + dispatch(stateSave()); + }; +} + +/** + * Saves Explore state to URL using the `left` and `right` parameters. + * If split view is not active, `right` will not be set. + */ export function stateSave() { return (dispatch, getState) => { const { left, right, split } = getState().explore; @@ -834,3 +704,39 @@ export function stateSave() { dispatch(updateLocation({ query: urlStates })); }; } + +/** + * Expand/collapse the graph result viewer. When collapsed, graph queries won't be run. + */ +export function toggleGraph(exploreId: ExploreId): ThunkResult { + return (dispatch, getState) => { + dispatch({ type: ActionTypes.ToggleGraph, exploreId }); + if (getState().explore[exploreId].showingGraph) { + dispatch(runQueries(exploreId)); + } + }; +} + +/** + * Expand/collapse the logs result viewer. When collapsed, log queries won't be run. + */ +export function toggleLogs(exploreId: ExploreId): ThunkResult { + return (dispatch, getState) => { + dispatch({ type: ActionTypes.ToggleLogs, exploreId }); + if (getState().explore[exploreId].showingLogs) { + dispatch(runQueries(exploreId)); + } + }; +} + +/** + * Expand/collapse the table result viewer. When collapsed, table queries won't be run. + */ +export function toggleTable(exploreId: ExploreId): ThunkResult { + return (dispatch, getState) => { + dispatch({ type: ActionTypes.ToggleTable, exploreId }); + if (getState().explore[exploreId].showingTable) { + dispatch(runQueries(exploreId)); + } + }; +} diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 91d5c4cf925..73790ba14bc 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -7,7 +7,7 @@ import { import { ExploreItemState, ExploreState, QueryTransaction } from 'app/types/explore'; import { DataQuery } from 'app/types/series'; -import { Action, ActionTypes } from './actions'; +import { Action, ActionTypes } from './actionTypes'; export const DEFAULT_RANGE = { from: 'now-6h', @@ -62,14 +62,17 @@ const itemReducer = (state, action: Action): ExploreItemState => { case ActionTypes.AddQueryRow: { const { initialQueries, modifiedQueries, queryTransactions } = state; const { index, query } = action; - modifiedQueries[index + 1] = query; - const nextQueries = [ - ...initialQueries.slice(0, index + 1), - { ...modifiedQueries[index + 1] }, + // Add new query row after given index, keep modifications of existing rows + const nextModifiedQueries = [ + ...modifiedQueries.slice(0, index + 1), + { ...query }, ...initialQueries.slice(index + 1), ]; + // Add to initialQueries, which will cause a new row to be rendered + const nextQueries = [...initialQueries.slice(0, index + 1), { ...query }, ...initialQueries.slice(index + 1)]; + // Ongoing transactions need to update their row indices const nextQueryTransactions = queryTransactions.map(qt => { if (qt.rowIndex > index) { @@ -83,9 +86,9 @@ const itemReducer = (state, action: Action): ExploreItemState => { return { ...state, - modifiedQueries, initialQueries: nextQueries, logsHighlighterExpressions: undefined, + modifiedQueries: nextModifiedQueries, queryTransactions: nextQueryTransactions, }; } @@ -94,29 +97,33 @@ const itemReducer = (state, action: Action): ExploreItemState => { const { initialQueries, queryTransactions } = state; let { modifiedQueries } = state; const { query, index, override } = action; + + // Fast path: only change modifiedQueries to not trigger an update modifiedQueries[index] = query; - if (override) { - const nextQuery: DataQuery = { - ...query, - ...generateEmptyQuery(index), - }; - const nextQueries = [...initialQueries]; - nextQueries[index] = nextQuery; - modifiedQueries = [...nextQueries]; - - // Discard ongoing transaction related to row query - const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); - + if (!override) { return { ...state, - initialQueries: nextQueries, - modifiedQueries: nextQueries.slice(), - queryTransactions: nextQueryTransactions, + modifiedQueries, }; } + + // Override path: queries are completely reset + const nextQuery: DataQuery = { + ...query, + ...generateEmptyQuery(index), + }; + const nextQueries = [...initialQueries]; + nextQueries[index] = nextQuery; + modifiedQueries = [...nextQueries]; + + // Discard ongoing transaction related to row query + const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); + return { ...state, - modifiedQueries, + initialQueries: nextQueries, + modifiedQueries: nextQueries.slice(), + queryTransactions: nextQueryTransactions, }; } @@ -138,58 +145,17 @@ const itemReducer = (state, action: Action): ExploreItemState => { }; } - case ActionTypes.ClickClear: { + case ActionTypes.ClearQueries: { const queries = ensureQueries(); return { ...state, initialQueries: queries.slice(), modifiedQueries: queries.slice(), + queryTransactions: [], showingStartPage: Boolean(state.StartPage), }; } - case ActionTypes.ClickExample: { - const modifiedQueries = [action.query]; - return { ...state, initialQueries: modifiedQueries.slice(), modifiedQueries }; - } - - case ActionTypes.ClickGraphButton: { - 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 { ...state, queryTransactions: nextQueryTransactions, showingGraph }; - } - - case ActionTypes.ClickLogsButton: { - 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 { ...state, queryTransactions: nextQueryTransactions, showingLogs }; - } - - case ActionTypes.ClickTableButton: { - const showingTable = !state.showingTable; - if (showingTable) { - return { ...state, showingTable, queryTransactions: state.queryTransactions }; - } - - // Toggle off needs discarding of table queries and results - const nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Table'); - const results = calculateResultsFromQueryTransactions( - nextQueryTransactions, - state.datasourceInstance, - state.queryIntervals.intervalMs - ); - - return { ...state, ...results, queryTransactions: nextQueryTransactions, showingTable }; - } - case ActionTypes.HighlightLogsExpression: { const { expressions } = action; return { ...state, logsHighlighterExpressions: expressions }; @@ -248,7 +214,7 @@ const itemReducer = (state, action: Action): ExploreItemState => { case ActionTypes.ModifyQueries: { const { initialQueries, modifiedQueries, queryTransactions } = state; - const { action: modification, index, modifier } = action as any; + const { modification, index, modifier } = action as any; let nextQueries: DataQuery[]; let nextQueryTransactions; if (index === undefined) { @@ -290,37 +256,6 @@ const itemReducer = (state, action: Action): ExploreItemState => { }; } - case ActionTypes.RemoveQueryRow: { - const { datasourceInstance, initialQueries, queryIntervals, queryTransactions } = state; - let { modifiedQueries } = state; - const { index } = action; - - modifiedQueries = [...modifiedQueries.slice(0, index), ...modifiedQueries.slice(index + 1)]; - - if (initialQueries.length <= 1) { - return state; - } - - const nextQueries = [...initialQueries.slice(0, index), ...initialQueries.slice(index + 1)]; - - // Discard transactions related to row query - const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); - const results = calculateResultsFromQueryTransactions( - nextQueryTransactions, - datasourceInstance, - queryIntervals.intervalMs - ); - - return { - ...state, - ...results, - initialQueries: nextQueries, - logsHighlighterExpressions: undefined, - modifiedQueries: nextQueries.slice(), - queryTransactions: nextQueryTransactions, - }; - } - case ActionTypes.QueryTransactionFailure: { const { queryTransactions } = action; return { @@ -373,6 +308,41 @@ const itemReducer = (state, action: Action): ExploreItemState => { }; } + case ActionTypes.RemoveQueryRow: { + const { datasourceInstance, initialQueries, queryIntervals, queryTransactions } = state; + let { modifiedQueries } = state; + const { index } = action; + + modifiedQueries = [...modifiedQueries.slice(0, index), ...modifiedQueries.slice(index + 1)]; + + if (initialQueries.length <= 1) { + return state; + } + + const nextQueries = [...initialQueries.slice(0, index), ...initialQueries.slice(index + 1)]; + + // Discard transactions related to row query + const nextQueryTransactions = queryTransactions.filter(qt => qt.rowIndex !== index); + const results = calculateResultsFromQueryTransactions( + nextQueryTransactions, + datasourceInstance, + queryIntervals.intervalMs + ); + + return { + ...state, + ...results, + initialQueries: nextQueries, + logsHighlighterExpressions: undefined, + modifiedQueries: nextQueries.slice(), + queryTransactions: nextQueryTransactions, + }; + } + + case ActionTypes.RunQueriesEmpty: { + return { ...state, queryTransactions: [] }; + } + case ActionTypes.ScanRange: { return { ...state, scanRange: action.range }; } @@ -386,6 +356,48 @@ const itemReducer = (state, action: Action): ExploreItemState => { const nextQueryTransactions = queryTransactions.filter(qt => qt.scanning && !qt.done); return { ...state, queryTransactions: nextQueryTransactions, scanning: false, scanRange: undefined }; } + + case ActionTypes.SetQueries: { + const { queries } = action; + return { ...state, initialQueries: queries.slice(), modifiedQueries: queries.slice() }; + } + + case ActionTypes.ToggleGraph: { + 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 { ...state, queryTransactions: nextQueryTransactions, showingGraph }; + } + + case ActionTypes.ToggleLogs: { + 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 { ...state, queryTransactions: nextQueryTransactions, showingLogs }; + } + + case ActionTypes.ToggleTable: { + const showingTable = !state.showingTable; + if (showingTable) { + return { ...state, showingTable, queryTransactions: state.queryTransactions }; + } + + // Toggle off needs discarding of table queries and results + const nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Table'); + const results = calculateResultsFromQueryTransactions( + nextQueryTransactions, + state.datasourceInstance, + state.queryIntervals.intervalMs + ); + + return { ...state, ...results, queryTransactions: nextQueryTransactions, showingTable }; + } } return state; @@ -397,14 +409,14 @@ const itemReducer = (state, action: Action): ExploreItemState => { */ export const exploreReducer = (state = initialExploreState, action: Action): ExploreState => { switch (action.type) { - case ActionTypes.ClickCloseSplit: { + case ActionTypes.SplitClose: { return { ...state, split: false, }; } - case ActionTypes.ClickSplit: { + case ActionTypes.SplitOpen: { return { ...state, split: true, diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 3cef4124ee4..5636bb3acdb 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -83,43 +83,160 @@ export enum ExploreId { right = 'right', } +/** + * Global Explore state + */ export interface ExploreState { + /** + * True if split view is active. + */ split: boolean; + /** + * Explore state of the left split (left is default in non-split view). + */ left: ExploreItemState; + /** + * Explore state of the right area in split view. + */ right: ExploreItemState; } export interface ExploreItemState { + /** + * React component to be shown when no queries have been run yet, e.g., for a query language cheat sheet. + */ StartPage?: any; + /** + * Width used for calculating the graph interval (can't have more datapoints than pixels) + */ containerWidth: number; + /** + * Datasource instance that has been selected. Datasource-specific logic can be run on this object. + */ datasourceInstance: any; + /** + * Error to be shown when datasource loading or testing failed. + */ datasourceError: string; + /** + * True if the datasource is loading. `null` if the loading has not started yet. + */ datasourceLoading: boolean | null; + /** + * True if there is no datasource to be selected. + */ datasourceMissing: boolean; + /** + * Emitter to send events to the rest of Grafana. + */ eventBridge?: Emitter; + /** + * List of datasources to be shown in the datasource selector. + */ exploreDatasources: DataSourceSelectItem[]; + /** + * List of timeseries to be shown in the Explore graph result viewer. + */ graphResult?: any[]; + /** + * History of recent queries. Datasource-specific and initialized via localStorage. + */ history: HistoryItem[]; + /** + * Initial datasource for this Explore, e.g., set via URL. + */ initialDatasource?: string; + /** + * Initial queries for this Explore, e.g., set via URL. Each query will be + * converted to a query row. Query edits should be tracked in `modifiedQueries` though. + */ initialQueries: DataQuery[]; + /** + * True if this Explore area has been initialized. + * Used to distinguish URL state injection versus split view state injection. + */ initialized: boolean; + /** + * Log line substrings to be highlighted as you type in a query field. + * Currently supports only the first query row. + */ logsHighlighterExpressions?: string[]; + /** + * Log query result to be displayed in the logs result viewer. + */ logsResult?: LogsModel; + /** + * Copy of `initialQueries` that tracks user edits. + * Don't connect this property to a react component as it is updated on every query change. + * Used when running queries. Needs to be reset to `initialQueries` when those are reset as well. + */ modifiedQueries: DataQuery[]; + /** + * Query intervals for graph queries to determine how many datapoints to return. + * Needs to be updated when `datasourceInstance` or `containerWidth` is changed. + */ queryIntervals: QueryIntervals; + /** + * List of query transaction to track query duration and query result. + * Graph/Logs/Table results are calculated on the fly from the transaction, + * based on the transaction's result types. Transaction also holds the row index + * so that results can be dropped and re-computed without running queries again + * when query rows are removed. + */ queryTransactions: QueryTransaction[]; + /** + * Tracks datasource when selected in the datasource selector. + * Allows the selection to be discarded if something went wrong during the asynchronous + * loading of the datasource. + */ requestedDatasourceId?: number; + /** + * Time range for this Explore. Managed by the time picker and used by all query runs. + */ range: TimeRange | RawTimeRange; + /** + * Scanner function that calculates a new range, triggers a query run, and returns the new range. + */ scanner?: RangeScanner; + /** + * True if scanning for more results is active. + */ scanning?: boolean; + /** + * Current scanning range to be shown to the user while scanning is active. + */ scanRange?: RawTimeRange; + /** + * True if graph result viewer is expanded. Query runs will contain graph queries. + */ showingGraph: boolean; + /** + * True if logs result viewer is expanded. Query runs will contain logs queries. + */ showingLogs: boolean; + /** + * True StartPage needs to be shown. Typically set to `false` once queries have been run. + */ showingStartPage?: boolean; + /** + * True if table result viewer is expanded. Query runs will contain table queries. + */ showingTable: boolean; + /** + * True if `datasourceInstance` supports graph queries. + */ supportsGraph: boolean | null; + /** + * True if `datasourceInstance` supports logs queries. + */ supportsLogs: boolean | null; + /** + * True if `datasourceInstance` supports table queries. + */ supportsTable: boolean | null; + /** + * Table model that combines all query table results into a single table. + */ tableResult?: TableModel; }