Refactor of action, actionTypes and reducer

This commit is contained in:
Hugo Häggmark 2019-02-04 07:47:10 +01:00
parent 2d0fd96621
commit 1f5bb76718
7 changed files with 698 additions and 694 deletions

View File

@ -18,15 +18,7 @@ import TableContainer from './TableContainer';
import TimePicker, { parseTime } from './TimePicker';
// Actions
import {
changeSize,
changeTime,
initializeExplore,
modifyQueries,
scanStart,
scanStop,
setQueries,
} from './state/actions';
import { changeSize, changeTime, initializeExplore, modifyQueries, scanStart, setQueries } from './state/actions';
// Types
import { RawTimeRange, TimeRange, DataQuery } from '@grafana/ui';
@ -35,6 +27,7 @@ import { StoreState } from 'app/types';
import { LAST_USED_DATASOURCE_KEY, ensureQueries, DEFAULT_RANGE } from 'app/core/utils/explore';
import { Emitter } from 'app/core/utils/emitter';
import { ExploreToolbar } from './ExploreToolbar';
import { scanStopAction } from './state/actionTypes';
interface ExploreProps {
StartPage?: any;
@ -54,7 +47,7 @@ interface ExploreProps {
scanning?: boolean;
scanRange?: RawTimeRange;
scanStart: typeof scanStart;
scanStop: typeof scanStop;
scanStopAction: typeof scanStopAction;
setQueries: typeof setQueries;
split: boolean;
showingStartPage?: boolean;
@ -171,7 +164,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
};
onStopScanning = () => {
this.props.scanStop(this.props.exploreId);
this.props.scanStopAction({ exploreId: this.props.exploreId });
};
render() {
@ -281,7 +274,7 @@ const mapDispatchToProps = {
initializeExplore,
modifyQueries,
scanStart,
scanStop,
scanStopAction,
setQueries,
};

View File

@ -9,20 +9,14 @@ import QueryEditor from './QueryEditor';
import QueryTransactionStatus from './QueryTransactionStatus';
// Actions
import {
addQueryRow,
changeQuery,
highlightLogsExpression,
modifyQueries,
removeQueryRow,
runQueries,
} from './state/actions';
import { changeQuery, modifyQueries, runQueries, addQueryRow } from './state/actions';
// Types
import { StoreState } from 'app/types';
import { RawTimeRange, DataQuery, ExploreDataSourceApi, QueryHint, QueryFixAction } from '@grafana/ui';
import { QueryTransaction, HistoryItem, ExploreItemState, ExploreId } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
@ -38,7 +32,7 @@ interface QueryRowProps {
className?: string;
exploreId: ExploreId;
datasourceInstance: ExploreDataSourceApi;
highlightLogsExpression: typeof highlightLogsExpression;
highlightLogsExpressionAction: typeof highlightLogsExpressionAction;
history: HistoryItem[];
index: number;
initialQuery: DataQuery;
@ -46,7 +40,7 @@ interface QueryRowProps {
queryTransactions: QueryTransaction[];
exploreEvents: Emitter;
range: RawTimeRange;
removeQueryRow: typeof removeQueryRow;
removeQueryRowAction: typeof removeQueryRowAction;
runQueries: typeof runQueries;
}
@ -88,14 +82,15 @@ export class QueryRow extends PureComponent<QueryRowProps> {
onClickRemoveButton = () => {
const { exploreId, index } = this.props;
this.props.removeQueryRow(exploreId, index);
this.props.removeQueryRowAction({ exploreId, index });
};
updateLogsHighlights = _.debounce((value: DataQuery) => {
const { datasourceInstance } = this.props;
if (datasourceInstance.getHighlighterExpression) {
const { exploreId } = this.props;
const expressions = [datasourceInstance.getHighlighterExpression(value)];
this.props.highlightLogsExpression(this.props.exploreId, expressions);
this.props.highlightLogsExpressionAction({ exploreId, expressions });
}
}, 500);
@ -168,9 +163,9 @@ function mapStateToProps(state: StoreState, { exploreId, index }) {
const mapDispatchToProps = {
addQueryRow,
changeQuery,
highlightLogsExpression,
highlightLogsExpressionAction,
modifyQueries,
removeQueryRow,
removeQueryRowAction,
runQueries,
};

View File

@ -7,16 +7,16 @@ import { StoreState } from 'app/types';
import { ExploreId, ExploreUrlState } from 'app/types/explore';
import { parseUrlState } from 'app/core/utils/explore';
import { initializeExploreSplit, resetExplore } from './state/actions';
import ErrorBoundary from './ErrorBoundary';
import Explore from './Explore';
import { CustomScrollbar } from '@grafana/ui';
import { initializeExploreSplitAction, resetExploreAction } from './state/actionTypes';
interface WrapperProps {
initializeExploreSplit: typeof initializeExploreSplit;
initializeExploreSplitAction: typeof initializeExploreSplitAction;
split: boolean;
updateLocation: typeof updateLocation;
resetExplore: typeof resetExplore;
resetExploreAction: typeof resetExploreAction;
urlStates: { [key: string]: string };
}
@ -39,12 +39,12 @@ export class Wrapper extends Component<WrapperProps> {
componentDidMount() {
if (this.initialSplit) {
this.props.initializeExploreSplit();
this.props.initializeExploreSplitAction();
}
}
componentWillUnmount() {
this.props.resetExplore();
this.props.resetExploreAction();
}
render() {
@ -77,9 +77,9 @@ const mapStateToProps = (state: StoreState) => {
};
const mapDispatchToProps = {
initializeExploreSplit,
initializeExploreSplitAction,
updateLocation,
resetExplore,
resetExploreAction,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(Wrapper));

View File

@ -1,6 +1,13 @@
// Types
import { Emitter } from 'app/core/core';
import { RawTimeRange, TimeRange, DataQuery, DataSourceSelectItem, DataSourceApi, QueryFixAction } from '@grafana/ui/src/types';
import {
RawTimeRange,
TimeRange,
DataQuery,
DataSourceSelectItem,
DataSourceApi,
QueryFixAction,
} from '@grafana/ui/src/types';
import {
ExploreId,
ExploreItemState,
@ -9,233 +16,26 @@ import {
ResultType,
QueryTransaction,
} from 'app/types/explore';
import { actionCreatorFactory, noPayloadActionCreatorFactory, ActionOf } from 'app/core/redux/actionCreatorFactory';
/** Higher order actions
*
*/
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',
UpdateDatasourceInstance = 'explore/UPDATE_DATASOURCE_INSTANCE',
ResetExplore = 'explore/RESET_EXPLORE',
QueriesImported = 'explore/QueriesImported',
}
export interface AddQueryRowAction {
type: ActionTypes.AddQueryRow;
payload: {
exploreId: ExploreId;
index: number;
query: DataQuery;
};
}
export interface ChangeQueryAction {
type: ActionTypes.ChangeQuery;
payload: {
exploreId: ExploreId;
query: DataQuery;
index: number;
override: boolean;
};
}
export interface ChangeSizeAction {
type: ActionTypes.ChangeSize;
payload: {
exploreId: ExploreId;
width: number;
height: number;
};
}
export interface ChangeTimeAction {
type: ActionTypes.ChangeTime;
payload: {
exploreId: ExploreId;
range: TimeRange;
};
}
export interface ClearQueriesAction {
type: ActionTypes.ClearQueries;
payload: {
exploreId: ExploreId;
};
}
export interface HighlightLogsExpressionAction {
type: ActionTypes.HighlightLogsExpression;
payload: {
exploreId: ExploreId;
expressions: string[];
};
}
export interface InitializeExploreAction {
type: ActionTypes.InitializeExplore;
payload: {
exploreId: ExploreId;
containerWidth: number;
eventBridge: Emitter;
exploreDatasources: DataSourceSelectItem[];
queries: DataQuery[];
range: RawTimeRange;
};
}
export interface InitializeExploreSplitAction {
type: ActionTypes.InitializeExploreSplit;
}
export interface LoadDatasourceFailureAction {
type: ActionTypes.LoadDatasourceFailure;
payload: {
exploreId: ExploreId;
error: string;
};
}
export interface LoadDatasourcePendingAction {
type: ActionTypes.LoadDatasourcePending;
payload: {
exploreId: ExploreId;
requestedDatasourceName: string;
};
}
export interface LoadDatasourceMissingAction {
type: ActionTypes.LoadDatasourceMissing;
payload: {
exploreId: ExploreId;
};
}
export interface LoadDatasourceSuccessAction {
type: ActionTypes.LoadDatasourceSuccess;
payload: {
exploreId: ExploreId;
StartPage?: any;
datasourceInstance: any;
history: HistoryItem[];
logsHighlighterExpressions?: any[];
showingStartPage: boolean;
supportsGraph: boolean;
supportsLogs: boolean;
supportsTable: boolean;
};
}
export interface ModifyQueriesAction {
type: ActionTypes.ModifyQueries;
payload: {
exploreId: ExploreId;
modification: QueryFixAction;
index: number;
modifier: (queries: DataQuery[], modification: QueryFixAction) => DataQuery[];
};
}
export interface QueryTransactionFailureAction {
type: ActionTypes.QueryTransactionFailure;
payload: {
exploreId: ExploreId;
queryTransactions: QueryTransaction[];
};
}
export interface QueryTransactionStartAction {
type: ActionTypes.QueryTransactionStart;
payload: {
exploreId: ExploreId;
resultType: ResultType;
rowIndex: number;
transaction: QueryTransaction;
};
}
export interface QueryTransactionSuccessAction {
type: ActionTypes.QueryTransactionSuccess;
payload: {
exploreId: ExploreId;
history: HistoryItem[];
queryTransactions: QueryTransaction[];
};
}
export interface RemoveQueryRowAction {
type: ActionTypes.RemoveQueryRow;
payload: {
exploreId: ExploreId;
index: number;
};
}
export interface RunQueriesEmptyAction {
type: ActionTypes.RunQueriesEmpty;
payload: {
exploreId: ExploreId;
};
}
export interface ScanStartAction {
type: ActionTypes.ScanStart;
payload: {
exploreId: ExploreId;
scanner: RangeScanner;
};
}
export interface ScanRangeAction {
type: ActionTypes.ScanRange;
payload: {
exploreId: ExploreId;
range: RawTimeRange;
};
}
export interface ScanStopAction {
type: ActionTypes.ScanStop;
payload: {
exploreId: ExploreId;
};
}
export interface SetQueriesAction {
type: ActionTypes.SetQueries;
payload: {
exploreId: ExploreId;
queries: DataQuery[];
};
payload: {};
}
export interface SplitCloseAction {
type: ActionTypes.SplitClose;
payload: {};
}
export interface SplitOpenAction {
@ -245,80 +45,384 @@ export interface SplitOpenAction {
};
}
export interface StateSaveAction {
type: ActionTypes.StateSave;
}
export interface ToggleTableAction {
type: ActionTypes.ToggleTable;
payload: {
exploreId: ExploreId;
};
}
export interface ToggleGraphAction {
type: ActionTypes.ToggleGraph;
payload: {
exploreId: ExploreId;
};
}
export interface ToggleLogsAction {
type: ActionTypes.ToggleLogs;
payload: {
exploreId: ExploreId;
};
}
export interface UpdateDatasourceInstanceAction {
type: ActionTypes.UpdateDatasourceInstance;
payload: {
exploreId: ExploreId;
datasourceInstance: DataSourceApi;
};
}
export interface ResetExploreAction {
type: ActionTypes.ResetExplore;
payload: {};
}
export interface QueriesImported {
type: ActionTypes.QueriesImported;
payload: {
exploreId: ExploreId;
queries: DataQuery[];
};
/** Lower order actions
*
*/
export interface AddQueryRowPayload {
exploreId: ExploreId;
index: number;
query: DataQuery;
}
export type Action =
| AddQueryRowAction
| ChangeQueryAction
| ChangeSizeAction
| ChangeTimeAction
| ClearQueriesAction
| HighlightLogsExpressionAction
| InitializeExploreAction
export interface ChangeQueryPayload {
exploreId: ExploreId;
query: DataQuery;
index: number;
override: boolean;
}
export interface ChangeSizePayload {
exploreId: ExploreId;
width: number;
height: number;
}
export interface ChangeTimePayload {
exploreId: ExploreId;
range: TimeRange;
}
export interface ClearQueriesPayload {
exploreId: ExploreId;
}
export interface HighlightLogsExpressionPayload {
exploreId: ExploreId;
expressions: string[];
}
export interface InitializeExplorePayload {
exploreId: ExploreId;
containerWidth: number;
eventBridge: Emitter;
exploreDatasources: DataSourceSelectItem[];
queries: DataQuery[];
range: RawTimeRange;
}
export interface LoadDatasourceFailurePayload {
exploreId: ExploreId;
error: string;
}
export interface LoadDatasourceMissingPayload {
exploreId: ExploreId;
}
export interface LoadDatasourcePendingPayload {
exploreId: ExploreId;
requestedDatasourceName: string;
}
export interface LoadDatasourceSuccessPayload {
exploreId: ExploreId;
StartPage?: any;
datasourceInstance: any;
history: HistoryItem[];
logsHighlighterExpressions?: any[];
showingStartPage: boolean;
supportsGraph: boolean;
supportsLogs: boolean;
supportsTable: boolean;
}
export interface ModifyQueriesPayload {
exploreId: ExploreId;
modification: QueryFixAction;
index: number;
modifier: (query: DataQuery, modification: QueryFixAction) => DataQuery;
}
export interface QueryTransactionFailurePayload {
exploreId: ExploreId;
queryTransactions: QueryTransaction[];
}
export interface QueryTransactionStartPayload {
exploreId: ExploreId;
resultType: ResultType;
rowIndex: number;
transaction: QueryTransaction;
}
export interface QueryTransactionSuccessPayload {
exploreId: ExploreId;
history: HistoryItem[];
queryTransactions: QueryTransaction[];
}
export interface RemoveQueryRowPayload {
exploreId: ExploreId;
index: number;
}
export interface RunQueriesEmptyPayload {
exploreId: ExploreId;
}
export interface ScanStartPayload {
exploreId: ExploreId;
scanner: RangeScanner;
}
export interface ScanRangePayload {
exploreId: ExploreId;
range: RawTimeRange;
}
export interface ScanStopPayload {
exploreId: ExploreId;
}
export interface SetQueriesPayload {
exploreId: ExploreId;
queries: DataQuery[];
}
export interface SplitOpenPayload {
itemState: ExploreItemState;
}
export interface ToggleTablePayload {
exploreId: ExploreId;
}
export interface ToggleGraphPayload {
exploreId: ExploreId;
}
export interface ToggleLogsPayload {
exploreId: ExploreId;
}
export interface UpdateDatasourceInstancePayload {
exploreId: ExploreId;
datasourceInstance: DataSourceApi;
}
export interface QueriesImportedPayload {
exploreId: ExploreId;
queries: DataQuery[];
}
/**
* Adds a query row after the row with the given index.
*/
export const addQueryRowAction = actionCreatorFactory<AddQueryRowPayload>('explore/ADD_QUERY_ROW').create();
/**
* Loads a new datasource identified by the given name.
*/
export const changeDatasourceAction = noPayloadActionCreatorFactory('explore/CHANGE_DATASOURCE').create();
/**
* 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 const changeQueryAction = actionCreatorFactory<ChangeQueryPayload>('explore/CHANGE_QUERY').create();
/**
* Keep track of the Explore container size, in particular the width.
* The width will be used to calculate graph intervals (number of datapoints).
*/
export const changeSizeAction = actionCreatorFactory<ChangeSizePayload>('explore/CHANGE_SIZE').create();
/**
* Change the time range of Explore. Usually called from the Timepicker or a graph interaction.
*/
export const changeTimeAction = actionCreatorFactory<ChangeTimePayload>('explore/CHANGE_TIME').create();
/**
* Clear all queries and results.
*/
export const clearQueriesAction = actionCreatorFactory<ClearQueriesPayload>('explore/CLEAR_QUERIES').create();
/**
* Highlight expressions in the log results
*/
export const highlightLogsExpressionAction = actionCreatorFactory<HighlightLogsExpressionPayload>(
'explore/HIGHLIGHT_LOGS_EXPRESSION'
).create();
/**
* 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 const initializeExploreAction = actionCreatorFactory<InitializeExplorePayload>(
'explore/INITIALIZE_EXPLORE'
).create();
/**
* Initialize the wrapper split state
*/
export const initializeExploreSplitAction = noPayloadActionCreatorFactory('explore/INITIALIZE_EXPLORE_SPLIT').create();
/**
* Display an error that happened during the selection of a datasource
*/
export const loadDatasourceFailureAction = actionCreatorFactory<LoadDatasourceFailurePayload>(
'explore/LOAD_DATASOURCE_FAILURE'
).create();
/**
* Display an error when no datasources have been configured
*/
export const loadDatasourceMissingAction = actionCreatorFactory<LoadDatasourceMissingPayload>(
'explore/LOAD_DATASOURCE_MISSING'
).create();
/**
* Start the async process of loading a datasource to display a loading indicator
*/
export const loadDatasourcePendingAction = actionCreatorFactory<LoadDatasourcePendingPayload>(
'explore/LOAD_DATASOURCE_PENDING'
).create();
/**
* 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 loadDatasourceSuccessAction = actionCreatorFactory<LoadDatasourceSuccessPayload>(
'explore/LOAD_DATASOURCE_SUCCESS'
).create();
/**
* 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 const modifyQueriesAction = actionCreatorFactory<ModifyQueriesPayload>('explore/MODIFY_QUERIES').create();
/**
* Mark a query transaction as failed with an error extracted from the query response.
* The transaction will be marked as `done`.
*/
export const queryTransactionFailureAction = actionCreatorFactory<QueryTransactionFailurePayload>(
'explore/QUERY_TRANSACTION_FAILURE'
).create();
/**
* 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 const queryTransactionStartAction = actionCreatorFactory<QueryTransactionStartPayload>(
'explore/QUERY_TRANSACTION_START'
).create();
/**
* 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 const queryTransactionSuccessAction = actionCreatorFactory<QueryTransactionSuccessPayload>(
'explore/QUERY_TRANSACTION_SUCCESS'
).create();
/**
* Remove query row of the given index, as well as associated query results.
*/
export const removeQueryRowAction = actionCreatorFactory<RemoveQueryRowPayload>('explore/REMOVE_QUERY_ROW').create();
export const runQueriesAction = noPayloadActionCreatorFactory('explore/RUN_QUERIES').create();
export const runQueriesEmptyAction = actionCreatorFactory<RunQueriesEmptyPayload>('explore/RUN_QUERIES_EMPTY').create();
/**
* 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 const scanStartAction = actionCreatorFactory<ScanStartPayload>('explore/SCAN_START').create();
export const scanRangeAction = actionCreatorFactory<ScanRangePayload>('explore/SCAN_RANGE').create();
/**
* Stop any scanning for more results.
*/
export const scanStopAction = actionCreatorFactory<ScanStopPayload>('explore/SCAN_STOP').create();
/**
* Reset queries to the given queries. Any modifications will be discarded.
* Use this action for clicks on query examples. Triggers a query run.
*/
export const setQueriesAction = actionCreatorFactory<SetQueriesPayload>('explore/SET_QUERIES').create();
/**
* Close the split view and save URL state.
*/
export const splitCloseAction = noPayloadActionCreatorFactory('explore/SPLIT_CLOSE').create();
/**
* 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 const splitOpenAction = actionCreatorFactory<SplitOpenPayload>('explore/SPLIT_OPEN').create();
export const stateSaveAction = noPayloadActionCreatorFactory('explore/STATE_SAVE').create();
/**
* Expand/collapse the table result viewer. When collapsed, table queries won't be run.
*/
export const toggleTableAction = actionCreatorFactory<ToggleTablePayload>('explore/TOGGLE_TABLE').create();
/**
* Expand/collapse the graph result viewer. When collapsed, graph queries won't be run.
*/
export const toggleGraphAction = actionCreatorFactory<ToggleGraphPayload>('explore/TOGGLE_GRAPH').create();
/**
* Expand/collapse the logs result viewer. When collapsed, log queries won't be run.
*/
export const toggleLogsAction = actionCreatorFactory<ToggleLogsPayload>('explore/TOGGLE_LOGS').create();
/**
* Updates datasource instance before datasouce loading has started
*/
export const updateDatasourceInstanceAction = actionCreatorFactory<UpdateDatasourceInstancePayload>(
'explore/UPDATE_DATASOURCE_INSTANCE'
).create();
/**
* Resets state for explore.
*/
export const resetExploreAction = noPayloadActionCreatorFactory('explore/RESET_EXPLORE').create();
export const queriesImportedAction = actionCreatorFactory<QueriesImportedPayload>('explore/QueriesImported').create();
export type HigherOrderAction =
| InitializeExploreSplitAction
| LoadDatasourceFailureAction
| LoadDatasourceMissingAction
| LoadDatasourcePendingAction
| LoadDatasourceSuccessAction
| ModifyQueriesAction
| QueryTransactionFailureAction
| QueryTransactionStartAction
| QueryTransactionSuccessAction
| RemoveQueryRowAction
| RunQueriesEmptyAction
| ScanRangeAction
| ScanStartAction
| ScanStopAction
| SetQueriesAction
| SplitCloseAction
| SplitOpenAction
| ToggleGraphAction
| ToggleLogsAction
| ToggleTableAction
| UpdateDatasourceInstanceAction
| ResetExploreAction
| QueriesImported;
| ActionOf<any>;
export type Action =
| ActionOf<AddQueryRowPayload>
| ActionOf<ChangeQueryPayload>
| ActionOf<ChangeSizePayload>
| ActionOf<ChangeTimePayload>
| ActionOf<ClearQueriesPayload>
| ActionOf<HighlightLogsExpressionPayload>
| ActionOf<InitializeExplorePayload>
| ActionOf<LoadDatasourceFailurePayload>
| ActionOf<LoadDatasourceMissingPayload>
| ActionOf<LoadDatasourcePendingPayload>
| ActionOf<LoadDatasourceSuccessPayload>
| ActionOf<ModifyQueriesPayload>
| ActionOf<QueryTransactionFailurePayload>
| ActionOf<QueryTransactionStartPayload>
| ActionOf<QueryTransactionSuccessPayload>
| ActionOf<RemoveQueryRowPayload>
| ActionOf<RunQueriesEmptyPayload>
| ActionOf<ScanStartPayload>
| ActionOf<ScanRangePayload>
| ActionOf<SetQueriesPayload>
| ActionOf<SplitOpenPayload>
| ActionOf<ToggleTablePayload>
| ActionOf<ToggleGraphPayload>
| ActionOf<ToggleLogsPayload>
| ActionOf<UpdateDatasourceInstancePayload>
| ActionOf<QueriesImportedPayload>;

View File

@ -32,40 +32,49 @@ import {
QueryHint,
QueryFixAction,
} from '@grafana/ui/src/types';
import { ExploreId, ExploreUrlState, RangeScanner, ResultType, QueryOptions } from 'app/types/explore';
import {
ExploreId,
ExploreUrlState,
RangeScanner,
ResultType,
QueryOptions,
QueryTransaction,
} from 'app/types/explore';
import {
Action as ThunkableAction,
ActionTypes,
AddQueryRowAction,
ChangeSizeAction,
HighlightLogsExpressionAction,
LoadDatasourceFailureAction,
LoadDatasourceMissingAction,
LoadDatasourcePendingAction,
LoadDatasourceSuccessAction,
QueryTransactionStartAction,
ScanStopAction,
UpdateDatasourceInstanceAction,
QueriesImported,
ModifyQueriesAction,
Action,
updateDatasourceInstanceAction,
changeQueryAction,
changeSizeAction,
ChangeSizePayload,
changeTimeAction,
scanStopAction,
clearQueriesAction,
initializeExploreAction,
loadDatasourceMissingAction,
loadDatasourceFailureAction,
loadDatasourcePendingAction,
queriesImportedAction,
LoadDatasourceSuccessPayload,
loadDatasourceSuccessAction,
modifyQueriesAction,
queryTransactionFailureAction,
queryTransactionStartAction,
queryTransactionSuccessAction,
scanRangeAction,
runQueriesEmptyAction,
scanStartAction,
setQueriesAction,
splitCloseAction,
splitOpenAction,
toggleGraphAction,
toggleLogsAction,
toggleTableAction,
addQueryRowAction,
AddQueryRowPayload,
} from './actionTypes';
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, ThunkableAction>;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
/**
* Adds a query row after the row with the given index.
*/
export function addQueryRow(exploreId: ExploreId, index: number): AddQueryRowAction {
// /**
// * Adds a query row after the row with the given index.
// */
export function addQueryRow(exploreId: ExploreId, index: number): ActionOf<AddQueryRowPayload> {
const query = generateEmptyQuery(index + 1);
return { type: ActionTypes.AddQueryRow, payload: { exploreId, index, query } };
return addQueryRowAction({ exploreId, index, query });
}
/**
@ -79,7 +88,7 @@ export function changeDatasource(exploreId: ExploreId, datasource: string): Thun
await dispatch(importQueries(exploreId, modifiedQueries, currentDataSourceInstance, newDataSourceInstance));
dispatch(updateDatasourceInstance(exploreId, newDataSourceInstance));
dispatch(updateDatasourceInstanceAction({ exploreId, datasourceInstance: newDataSourceInstance }));
dispatch(loadDatasource(exploreId, newDataSourceInstance));
};
}
@ -100,7 +109,7 @@ export function changeQuery(
query = { ...generateEmptyQuery(index) };
}
dispatch({ type: ActionTypes.ChangeQuery, payload: { exploreId, query, index, override } });
dispatch(changeQueryAction({ exploreId, query, index, override }));
if (override) {
dispatch(runQueries(exploreId));
}
@ -114,8 +123,8 @@ export function changeQuery(
export function changeSize(
exploreId: ExploreId,
{ height, width }: { height: number; width: number }
): ChangeSizeAction {
return { type: ActionTypes.ChangeSize, payload: { exploreId, height, width } };
): ActionOf<ChangeSizePayload> {
return changeSizeAction({ exploreId, height, width });
}
/**
@ -123,7 +132,7 @@ export function changeSize(
*/
export function changeTime(exploreId: ExploreId, range: TimeRange): ThunkResult<void> {
return dispatch => {
dispatch({ type: ActionTypes.ChangeTime, payload: { exploreId, range } });
dispatch(changeTimeAction({ exploreId, range }));
dispatch(runQueries(exploreId));
};
}
@ -133,19 +142,12 @@ export function changeTime(exploreId: ExploreId, range: TimeRange): ThunkResult<
*/
export function clearQueries(exploreId: ExploreId): ThunkResult<void> {
return dispatch => {
dispatch(scanStop(exploreId));
dispatch({ type: ActionTypes.ClearQueries, payload: { exploreId } });
dispatch(scanStopAction({ exploreId }));
dispatch(clearQueriesAction({ exploreId }));
dispatch(stateSave());
};
}
/**
* Highlight expressions in the log results
*/
export function highlightLogsExpression(exploreId: ExploreId, expressions: string[]): HighlightLogsExpressionAction {
return { type: ActionTypes.HighlightLogsExpression, payload: { 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.
@ -167,18 +169,16 @@ export function initializeExplore(
meta: ds.meta,
}));
dispatch({
type: ActionTypes.InitializeExplore,
payload: {
dispatch(
initializeExploreAction({
exploreId,
containerWidth,
datasourceName,
eventBridge,
exploreDatasources,
queries,
range,
},
});
})
);
if (exploreDatasources.length >= 1) {
let instance;
@ -195,75 +195,20 @@ export function initializeExplore(
instance = await getDatasourceSrv().get();
}
dispatch(updateDatasourceInstance(exploreId, instance));
dispatch(updateDatasourceInstanceAction({ exploreId, datasourceInstance: instance }));
dispatch(loadDatasource(exploreId, instance));
} else {
dispatch(loadDatasourceMissing(exploreId));
dispatch(loadDatasourceMissingAction({ exploreId }));
}
};
}
/**
* 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,
payload: {
exploreId,
error,
},
});
/**
* Display an error when no datasources have been configured
*/
export const loadDatasourceMissing = (exploreId: ExploreId): LoadDatasourceMissingAction => ({
type: ActionTypes.LoadDatasourceMissing,
payload: { exploreId },
});
/**
* Start the async process of loading a datasource to display a loading indicator
*/
export const loadDatasourcePending = (
exploreId: ExploreId,
requestedDatasourceName: string
): LoadDatasourcePendingAction => ({
type: ActionTypes.LoadDatasourcePending,
payload: {
exploreId,
requestedDatasourceName,
},
});
export const queriesImported = (exploreId: ExploreId, queries: DataQuery[]): QueriesImported => {
return {
type: ActionTypes.QueriesImported,
payload: {
exploreId,
queries,
},
};
};
/**
* 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,
): LoadDatasourceSuccessAction => {
export const loadDatasourceSuccess = (exploreId: ExploreId, instance: any): ActionOf<LoadDatasourceSuccessPayload> => {
// Capabilities
const supportsGraph = instance.meta.metrics;
const supportsLogs = instance.meta.logs;
@ -276,37 +221,18 @@ export const loadDatasourceSuccess = (
// Save last-used datasource
store.set(LAST_USED_DATASOURCE_KEY, instance.name);
return {
type: ActionTypes.LoadDatasourceSuccess,
payload: {
exploreId,
StartPage,
datasourceInstance: instance,
history,
showingStartPage: Boolean(StartPage),
supportsGraph,
supportsLogs,
supportsTable,
},
};
return loadDatasourceSuccessAction({
exploreId,
StartPage,
datasourceInstance: instance,
history,
showingStartPage: Boolean(StartPage),
supportsGraph,
supportsLogs,
supportsTable,
});
};
/**
* Updates datasource instance before datasouce loading has started
*/
export function updateDatasourceInstance(
exploreId: ExploreId,
instance: DataSourceApi
): UpdateDatasourceInstanceAction {
return {
type: ActionTypes.UpdateDatasourceInstance,
payload: {
exploreId,
datasourceInstance: instance,
},
};
}
export function importQueries(
exploreId: ExploreId,
queries: DataQuery[],
@ -332,7 +258,7 @@ export function importQueries(
...generateEmptyQuery(i),
}));
dispatch(queriesImported(exploreId, nextQueries));
dispatch(queriesImportedAction({ exploreId, queries: nextQueries }));
};
}
@ -344,7 +270,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T
const datasourceName = instance.name;
// Keep ID to track selection
dispatch(loadDatasourcePending(exploreId, datasourceName));
dispatch(loadDatasourcePendingAction({ exploreId, requestedDatasourceName: datasourceName }));
let datasourceError = null;
try {
@ -355,7 +281,7 @@ export function loadDatasource(exploreId: ExploreId, instance: DataSourceApi): T
}
if (datasourceError) {
dispatch(loadDatasourceFailure(exploreId, datasourceError));
dispatch(loadDatasourceFailureAction({ exploreId, error: datasourceError }));
return;
}
@ -392,11 +318,7 @@ export function modifyQueries(
modifier: any
): ThunkResult<void> {
return dispatch => {
const modifyQueryAction: ModifyQueriesAction = {
type: ActionTypes.ModifyQueries,
payload: { exploreId, modification, index, modifier },
};
dispatch(modifyQueryAction);
dispatch(modifyQueriesAction({ exploreId, modification, index, modifier }));
if (!modification.preventSubmit) {
dispatch(runQueries(exploreId));
}
@ -461,29 +383,10 @@ export function queryTransactionFailure(
return qt;
});
dispatch({
type: ActionTypes.QueryTransactionFailure,
payload: { exploreId, queryTransactions: nextQueryTransactions },
});
dispatch(queryTransactionFailureAction({ exploreId, queryTransactions: nextQueryTransactions }));
};
}
/**
* 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,
resultType: ResultType,
rowIndex: number
): QueryTransactionStartAction {
return { type: ActionTypes.QueryTransactionStart, payload: { 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.
@ -540,14 +443,13 @@ export function queryTransactionSuccess(
// Side-effect: Saving history in localstorage
const nextHistory = updateHistory(history, datasourceId, queries);
dispatch({
type: ActionTypes.QueryTransactionSuccess,
payload: {
dispatch(
queryTransactionSuccessAction({
exploreId,
history: nextHistory,
queryTransactions: nextQueryTransactions,
},
});
})
);
// Keep scanning for results if this was the last scanning transaction
if (scanning) {
@ -555,26 +457,16 @@ export function queryTransactionSuccess(
const other = nextQueryTransactions.find(qt => qt.scanning && !qt.done);
if (!other) {
const range = scanner();
dispatch({ type: ActionTypes.ScanRange, payload: { exploreId, range } });
dispatch(scanRangeAction({ exploreId, range }));
}
} else {
// We can stop scanning if we have a result
dispatch(scanStop(exploreId));
dispatch(scanStopAction({ exploreId }));
}
}
};
}
/**
* Remove query row of the given index, as well as associated query results.
*/
export function removeQueryRow(exploreId: ExploreId, index: number): ThunkResult<void> {
return dispatch => {
dispatch({ type: ActionTypes.RemoveQueryRow, payload: { exploreId, index } });
dispatch(runQueries(exploreId));
};
}
/**
* Main action to run queries and dispatches sub-actions based on which result viewers are active
*/
@ -592,7 +484,7 @@ export function runQueries(exploreId: ExploreId) {
} = getState().explore[exploreId];
if (!hasNonEmptyQuery(modifiedQueries)) {
dispatch({ type: ActionTypes.RunQueriesEmpty, payload: { exploreId } });
dispatch(runQueriesEmptyAction({ exploreId }));
dispatch(stateSave()); // Remember to saves to state and update location
return;
}
@ -673,7 +565,7 @@ function runQueriesForType(
queryIntervals,
scanning
);
dispatch(queryTransactionStart(exploreId, transaction, resultType, rowIndex));
dispatch(queryTransactionStartAction({ exploreId, resultType, rowIndex, transaction }));
try {
const now = Date.now();
const res = await datasourceInstance.query(transaction.options);
@ -697,21 +589,14 @@ function runQueriesForType(
export function scanStart(exploreId: ExploreId, scanner: RangeScanner): ThunkResult<void> {
return dispatch => {
// Register the scanner
dispatch({ type: ActionTypes.ScanStart, payload: { exploreId, scanner } });
dispatch(scanStartAction({ 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, payload: { exploreId, range } });
dispatch(scanRangeAction({ exploreId, range }));
};
}
/**
* Stop any scanning for more results.
*/
export function scanStop(exploreId: ExploreId): ScanStopAction {
return { type: ActionTypes.ScanStop, payload: { exploreId } };
}
/**
* Reset queries to the given queries. Any modifications will be discarded.
* Use this action for clicks on query examples. Triggers a query run.
@ -720,13 +605,7 @@ export function setQueries(exploreId: ExploreId, rawQueries: DataQuery[]): Thunk
return dispatch => {
// Inject react keys into query objects
const queries = rawQueries.map(q => ({ ...q, ...generateEmptyQuery() }));
dispatch({
type: ActionTypes.SetQueries,
payload: {
exploreId,
queries,
},
});
dispatch(setQueriesAction({ exploreId, queries }));
dispatch(runQueries(exploreId));
};
}
@ -736,7 +615,7 @@ export function setQueries(exploreId: ExploreId, rawQueries: DataQuery[]): Thunk
*/
export function splitClose(): ThunkResult<void> {
return dispatch => {
dispatch({ type: ActionTypes.SplitClose });
dispatch(splitCloseAction());
dispatch(stateSave());
};
}
@ -755,7 +634,7 @@ export function splitOpen(): ThunkResult<void> {
queryTransactions: [],
initialQueries: leftState.modifiedQueries.slice(),
};
dispatch({ type: ActionTypes.SplitOpen, payload: { itemState } });
dispatch(splitOpenAction({ itemState }));
dispatch(stateSave());
};
}
@ -791,7 +670,7 @@ export function stateSave() {
*/
export function toggleGraph(exploreId: ExploreId): ThunkResult<void> {
return (dispatch, getState) => {
dispatch({ type: ActionTypes.ToggleGraph, payload: { exploreId } });
dispatch(toggleGraphAction({ exploreId }));
if (getState().explore[exploreId].showingGraph) {
dispatch(runQueries(exploreId));
}
@ -803,7 +682,7 @@ export function toggleGraph(exploreId: ExploreId): ThunkResult<void> {
*/
export function toggleLogs(exploreId: ExploreId): ThunkResult<void> {
return (dispatch, getState) => {
dispatch({ type: ActionTypes.ToggleLogs, payload: { exploreId } });
dispatch(toggleLogsAction({ exploreId }));
if (getState().explore[exploreId].showingLogs) {
dispatch(runQueries(exploreId));
}
@ -815,18 +694,9 @@ export function toggleLogs(exploreId: ExploreId): ThunkResult<void> {
*/
export function toggleTable(exploreId: ExploreId): ThunkResult<void> {
return (dispatch, getState) => {
dispatch({ type: ActionTypes.ToggleTable, payload: { exploreId } });
dispatch(toggleTableAction({ exploreId }));
if (getState().explore[exploreId].showingTable) {
dispatch(runQueries(exploreId));
}
};
}
/**
* Resets state for explore.
*/
export function resetExplore(): ThunkResult<void> {
return dispatch => {
dispatch({ type: ActionTypes.ResetExplore, payload: {} });
};
}

View File

@ -1,42 +1,47 @@
import { Action, ActionTypes } from './actionTypes';
import { itemReducer, makeExploreItemState } from './reducers';
import { ExploreId } from 'app/types/explore';
import { ExploreId, ExploreItemState } from 'app/types/explore';
import { reducerTester } from 'test/core/redux/reducerTester';
import { scanStartAction, scanStopAction } from './actionTypes';
import { Reducer } from 'redux';
import { ActionOf } from 'app/core/redux/actionCreatorFactory';
describe('Explore item reducer', () => {
describe('scanning', () => {
test('should start scanning', () => {
let state = makeExploreItemState();
const action: Action = {
type: ActionTypes.ScanStart,
payload: {
exploreId: ExploreId.left,
scanner: jest.fn(),
},
const scanner = jest.fn();
const initalState = {
...makeExploreItemState(),
scanning: false,
scanner: undefined,
};
state = itemReducer(state, action);
expect(state.scanning).toBeTruthy();
expect(state.scanner).toBe(action.payload.scanner);
reducerTester()
.givenReducer(itemReducer as Reducer<ExploreItemState, ActionOf<any>>, initalState)
.whenActionIsDispatched(scanStartAction({ exploreId: ExploreId.left, scanner }))
.thenStateShouldEqual({
...makeExploreItemState(),
scanning: true,
scanner,
});
});
test('should stop scanning', () => {
let state = makeExploreItemState();
const start: Action = {
type: ActionTypes.ScanStart,
payload: {
exploreId: ExploreId.left,
scanner: jest.fn(),
},
const scanner = jest.fn();
const initalState = {
...makeExploreItemState(),
scanning: true,
scanner,
scanRange: {},
};
state = itemReducer(state, start);
expect(state.scanning).toBeTruthy();
const action: Action = {
type: ActionTypes.ScanStop,
payload: {
exploreId: ExploreId.left,
},
};
state = itemReducer(state, action);
expect(state.scanning).toBeFalsy();
expect(state.scanner).toBeUndefined();
reducerTester()
.givenReducer(itemReducer as Reducer<ExploreItemState, ActionOf<any>>, initalState)
.whenActionIsDispatched(scanStopAction({ exploreId: ExploreId.left }))
.thenStateShouldEqual({
...makeExploreItemState(),
scanning: false,
scanner: undefined,
scanRange: undefined,
});
});
});
});

View File

@ -7,7 +7,36 @@ import {
import { ExploreItemState, ExploreState, QueryTransaction } from 'app/types/explore';
import { DataQuery } from '@grafana/ui/src/types';
import { Action, ActionTypes } from './actionTypes';
import { HigherOrderAction, ActionTypes } from './actionTypes';
import { reducerFactory } from 'app/core/redux';
import {
addQueryRowAction,
changeQueryAction,
changeSizeAction,
changeTimeAction,
clearQueriesAction,
highlightLogsExpressionAction,
initializeExploreAction,
updateDatasourceInstanceAction,
loadDatasourceFailureAction,
loadDatasourceMissingAction,
loadDatasourcePendingAction,
loadDatasourceSuccessAction,
modifyQueriesAction,
queryTransactionFailureAction,
queryTransactionStartAction,
queryTransactionSuccessAction,
removeQueryRowAction,
runQueriesEmptyAction,
scanRangeAction,
scanStartAction,
scanStopAction,
setQueriesAction,
toggleGraphAction,
toggleLogsAction,
toggleTableAction,
queriesImportedAction,
} from './actionTypes';
export const DEFAULT_RANGE = {
from: 'now-6h',
@ -58,9 +87,10 @@ export const initialExploreState: ExploreState = {
/**
* Reducer for an Explore area, to be used by the global Explore reducer.
*/
export const itemReducer = (state, action: Action): ExploreItemState => {
switch (action.type) {
case ActionTypes.AddQueryRow: {
export const itemReducer = reducerFactory<ExploreItemState>({} as ExploreItemState)
.addMapper({
filter: addQueryRowAction,
mapper: (state, action): ExploreItemState => {
const { initialQueries, modifiedQueries, queryTransactions } = state;
const { index, query } = action.payload;
@ -77,10 +107,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
// Ongoing transactions need to update their row indices
const nextQueryTransactions = queryTransactions.map(qt => {
if (qt.rowIndex > index) {
return {
...qt,
rowIndex: qt.rowIndex + 1,
};
return { ...qt, rowIndex: qt.rowIndex + 1 };
}
return qt;
});
@ -92,9 +119,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
modifiedQueries: nextModifiedQueries,
queryTransactions: nextQueryTransactions,
};
}
case ActionTypes.ChangeQuery: {
},
})
.addMapper({
filter: changeQueryAction,
mapper: (state, action): ExploreItemState => {
const { initialQueries, queryTransactions } = state;
let { modifiedQueries } = state;
const { query, index, override } = action.payload;
@ -102,17 +131,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
// Fast path: only change modifiedQueries to not trigger an update
modifiedQueries[index] = query;
if (!override) {
return {
...state,
modifiedQueries,
};
return { ...state, modifiedQueries };
}
// Override path: queries are completely reset
const nextQuery: DataQuery = {
...query,
...generateEmptyQuery(index),
};
const nextQuery: DataQuery = { ...query, ...generateEmptyQuery(index) };
const nextQueries = [...initialQueries];
nextQueries[index] = nextQuery;
modifiedQueries = [...nextQueries];
@ -126,9 +149,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
modifiedQueries: nextQueries.slice(),
queryTransactions: nextQueryTransactions,
};
}
case ActionTypes.ChangeSize: {
},
})
.addMapper({
filter: changeSizeAction,
mapper: (state, action): ExploreItemState => {
const { range, datasourceInstance } = state;
let interval = '1s';
if (datasourceInstance && datasourceInstance.interval) {
@ -137,16 +162,17 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
const containerWidth = action.payload.width;
const queryIntervals = getIntervals(range, interval, containerWidth);
return { ...state, containerWidth, queryIntervals };
}
case ActionTypes.ChangeTime: {
return {
...state,
range: action.payload.range,
};
}
case ActionTypes.ClearQueries: {
},
})
.addMapper({
filter: changeTimeAction,
mapper: (state, action): ExploreItemState => {
return { ...state, range: action.payload.range };
},
})
.addMapper({
filter: clearQueriesAction,
mapper: (state): ExploreItemState => {
const queries = ensureQueries();
return {
...state,
@ -155,14 +181,18 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
queryTransactions: [],
showingStartPage: Boolean(state.StartPage),
};
}
case ActionTypes.HighlightLogsExpression: {
},
})
.addMapper({
filter: highlightLogsExpressionAction,
mapper: (state, action): ExploreItemState => {
const { expressions } = action.payload;
return { ...state, logsHighlighterExpressions: expressions };
}
case ActionTypes.InitializeExplore: {
},
})
.addMapper({
filter: initializeExploreAction,
mapper: (state, action): ExploreItemState => {
const { containerWidth, eventBridge, exploreDatasources, queries, range } = action.payload;
return {
...state,
@ -174,30 +204,37 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
initialized: true,
modifiedQueries: queries.slice(),
};
}
case ActionTypes.UpdateDatasourceInstance: {
},
})
.addMapper({
filter: updateDatasourceInstanceAction,
mapper: (state, action): ExploreItemState => {
const { datasourceInstance } = action.payload;
return {
...state,
datasourceInstance,
datasourceName: datasourceInstance.name,
};
}
case ActionTypes.LoadDatasourceFailure: {
return { ...state, datasourceInstance };
/*datasourceName: datasourceInstance.name removed after refactor, datasourceName does not exists on ExploreItemState */
},
})
.addMapper({
filter: loadDatasourceFailureAction,
mapper: (state, action): ExploreItemState => {
return { ...state, datasourceError: action.payload.error, datasourceLoading: false };
}
case ActionTypes.LoadDatasourceMissing: {
},
})
.addMapper({
filter: loadDatasourceMissingAction,
mapper: (state): ExploreItemState => {
return { ...state, datasourceMissing: true, datasourceLoading: false };
}
case ActionTypes.LoadDatasourcePending: {
},
})
.addMapper({
filter: loadDatasourcePendingAction,
mapper: (state, action): ExploreItemState => {
return { ...state, datasourceLoading: true, requestedDatasourceName: action.payload.requestedDatasourceName };
}
case ActionTypes.LoadDatasourceSuccess: {
},
})
.addMapper({
filter: loadDatasourceSuccessAction,
mapper: (state, action): ExploreItemState => {
const { containerWidth, range } = state;
const {
StartPage,
@ -226,9 +263,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
logsHighlighterExpressions: undefined,
queryTransactions: [],
};
}
case ActionTypes.ModifyQueries: {
},
})
.addMapper({
filter: modifyQueriesAction,
mapper: (state, action): ExploreItemState => {
const { initialQueries, modifiedQueries, queryTransactions } = state;
const { modification, index, modifier } = action.payload;
let nextQueries: DataQuery[];
@ -246,12 +285,7 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
nextQueries = initialQueries.map((query, i) => {
// Synchronize all queries with local query cache to ensure consistency
// TODO still needed?
return i === index
? {
...modifier(modifiedQueries[i], modification),
...generateEmptyQuery(i),
}
: query;
return i === index ? { ...modifier(modifiedQueries[i], modification), ...generateEmptyQuery(i) } : query;
});
nextQueryTransactions = queryTransactions
// Consume the hint corresponding to the action
@ -270,18 +304,18 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
modifiedQueries: nextQueries.slice(),
queryTransactions: nextQueryTransactions,
};
}
case ActionTypes.QueryTransactionFailure: {
},
})
.addMapper({
filter: queryTransactionFailureAction,
mapper: (state, action): ExploreItemState => {
const { queryTransactions } = action.payload;
return {
...state,
queryTransactions,
showingStartPage: false,
};
}
case ActionTypes.QueryTransactionStart: {
return { ...state, queryTransactions, showingStartPage: false };
},
})
.addMapper({
filter: queryTransactionStartAction,
mapper: (state, action): ExploreItemState => {
const { queryTransactions } = state;
const { resultType, rowIndex, transaction } = action.payload;
// Discarding existing transactions of same type
@ -292,14 +326,12 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
// Append new transaction
const nextQueryTransactions: QueryTransaction[] = [...remainingTransactions, transaction];
return {
...state,
queryTransactions: nextQueryTransactions,
showingStartPage: false,
};
}
case ActionTypes.QueryTransactionSuccess: {
return { ...state, queryTransactions: nextQueryTransactions, showingStartPage: false };
},
})
.addMapper({
filter: queryTransactionSuccessAction,
mapper: (state, action): ExploreItemState => {
const { datasourceInstance, queryIntervals } = state;
const { history, queryTransactions } = action.payload;
const results = calculateResultsFromQueryTransactions(
@ -308,16 +340,12 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
queryIntervals.intervalMs
);
return {
...state,
...results,
history,
queryTransactions,
showingStartPage: false,
};
}
case ActionTypes.RemoveQueryRow: {
return { ...state, ...results, history, queryTransactions, showingStartPage: false };
},
})
.addMapper({
filter: removeQueryRowAction,
mapper: (state, action): ExploreItemState => {
const { datasourceInstance, initialQueries, queryIntervals, queryTransactions } = state;
let { modifiedQueries } = state;
const { index } = action.payload;
@ -346,21 +374,29 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
modifiedQueries: nextQueries.slice(),
queryTransactions: nextQueryTransactions,
};
}
case ActionTypes.RunQueriesEmpty: {
},
})
.addMapper({
filter: runQueriesEmptyAction,
mapper: (state): ExploreItemState => {
return { ...state, queryTransactions: [] };
}
case ActionTypes.ScanRange: {
},
})
.addMapper({
filter: scanRangeAction,
mapper: (state, action): ExploreItemState => {
return { ...state, scanRange: action.payload.range };
}
case ActionTypes.ScanStart: {
},
})
.addMapper({
filter: scanStartAction,
mapper: (state, action): ExploreItemState => {
return { ...state, scanning: true, scanner: action.payload.scanner };
}
case ActionTypes.ScanStop: {
},
})
.addMapper({
filter: scanStopAction,
mapper: (state): ExploreItemState => {
const { queryTransactions } = state;
const nextQueryTransactions = queryTransactions.filter(qt => qt.scanning && !qt.done);
return {
@ -370,14 +406,18 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
scanRange: undefined,
scanner: undefined,
};
}
case ActionTypes.SetQueries: {
},
})
.addMapper({
filter: setQueriesAction,
mapper: (state, action): ExploreItemState => {
const { queries } = action.payload;
return { ...state, initialQueries: queries.slice(), modifiedQueries: queries.slice() };
}
case ActionTypes.ToggleGraph: {
},
})
.addMapper({
filter: toggleGraphAction,
mapper: (state): ExploreItemState => {
const showingGraph = !state.showingGraph;
let nextQueryTransactions = state.queryTransactions;
if (!showingGraph) {
@ -385,9 +425,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Graph');
}
return { ...state, queryTransactions: nextQueryTransactions, showingGraph };
}
case ActionTypes.ToggleLogs: {
},
})
.addMapper({
filter: toggleLogsAction,
mapper: (state): ExploreItemState => {
const showingLogs = !state.showingLogs;
let nextQueryTransactions = state.queryTransactions;
if (!showingLogs) {
@ -395,9 +437,11 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
nextQueryTransactions = state.queryTransactions.filter(qt => qt.resultType !== 'Logs');
}
return { ...state, queryTransactions: nextQueryTransactions, showingLogs };
}
case ActionTypes.ToggleTable: {
},
})
.addMapper({
filter: toggleTableAction,
mapper: (state): ExploreItemState => {
const showingTable = !state.showingTable;
if (showingTable) {
return { ...state, showingTable, queryTransactions: state.queryTransactions };
@ -412,25 +456,21 @@ export const itemReducer = (state, action: Action): ExploreItemState => {
);
return { ...state, ...results, queryTransactions: nextQueryTransactions, showingTable };
}
case ActionTypes.QueriesImported: {
return {
...state,
initialQueries: action.payload.queries,
modifiedQueries: action.payload.queries.slice(),
};
}
}
return state;
};
},
})
.addMapper({
filter: queriesImportedAction,
mapper: (state, action): ExploreItemState => {
return { ...state, initialQueries: action.payload.queries, modifiedQueries: action.payload.queries.slice() };
},
})
.create();
/**
* Global Explore reducer that handles multiple Explore areas (left and right).
* Actions that have an `exploreId` get routed to the ExploreItemReducer.
*/
export const exploreReducer = (state = initialExploreState, action: Action): ExploreState => {
export const exploreReducer = (state = initialExploreState, action: HigherOrderAction): ExploreState => {
switch (action.type) {
case ActionTypes.SplitClose: {
return { ...state, split: false };
@ -453,10 +493,7 @@ export const exploreReducer = (state = initialExploreState, action: Action): Exp
const { exploreId } = action.payload as any;
if (exploreId !== undefined) {
const exploreItemState = state[exploreId];
return {
...state,
[exploreId]: itemReducer(exploreItemState, action),
};
return { ...state, [exploreId]: itemReducer(exploreItemState, action) };
}
}