[Logs UI] Refactor query bar state to hooks (#52656)

* [Logs UI] Refactor query bar state to hooks

* Update typedef

* Typecheck fix

* Typecheck fix

* Simplify log filter state

* Remove WithLogFilter HOC and simplify hook further

* Rename js to ts

* Fix redirect imports

* Fix link-to test accuracy

* Fix link-to test

* Simplify destructuring signature

* Stylistic fixes

* Move URL state to hook

* Fix log filter URL state infinite loop

* Revert "Fix log filter URL state infinite loop"

This reverts commit 43302b354a.

* Revert "Move URL state to hook"

This reverts commit c61f5b190b.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Zacqary Adam Xeper 2020-01-03 15:45:11 -06:00 committed by GitHub
parent ace50d8d2b
commit a03f395b52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 223 additions and 328 deletions

View file

@ -4,23 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { useContext } from 'react';
import createContainer from 'constate';
import { ReduxStateContext } from '../../../utils/redux_context';
import { logFilterSelectors as logFilterReduxSelectors } from '../../../store/local/selectors';
export const useLogFilterState = () => {
const { local: state } = useContext(ReduxStateContext);
const filterQuery = logFilterReduxSelectors.selectLogFilterQueryAsJson(state);
return { filterQuery };
};
export interface LogFilterStateParams {
filterQuery: string | null;
}
export const logFilterInitialState = {
filterQuery: null,
};
export const LogFilterState = createContainer(useLogFilterState);
export * from './log_filter_state';
export * from './with_log_filter_url_state';

View file

@ -0,0 +1,101 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { useState, useMemo } from 'react';
import createContainer from 'constate';
import { IIndexPattern } from 'src/plugins/data/public';
import { esKuery } from '../../../../../../../../src/plugins/data/public';
import { convertKueryToElasticSearchQuery } from '../../../utils/kuery';
export interface KueryFilterQuery {
kind: 'kuery';
expression: string;
}
export interface SerializedFilterQuery {
query: KueryFilterQuery;
serializedQuery: string;
}
interface LogFilterInternalStateParams {
filterQuery: SerializedFilterQuery | null;
filterQueryDraft: KueryFilterQuery | null;
}
export const logFilterInitialState: LogFilterInternalStateParams = {
filterQuery: null,
filterQueryDraft: null,
};
export type LogFilterStateParams = Omit<LogFilterInternalStateParams, 'filterQuery'> & {
filterQuery: SerializedFilterQuery['serializedQuery'] | null;
filterQueryAsKuery: SerializedFilterQuery['query'] | null;
isFilterQueryDraftValid: boolean;
};
export interface LogFilterCallbacks {
setLogFilterQueryDraft: (expression: string) => void;
applyLogFilterQuery: (expression: string) => void;
}
export const useLogFilterState: (props: {
indexPattern: IIndexPattern;
}) => LogFilterStateParams & LogFilterCallbacks = ({ indexPattern }) => {
const [state, setState] = useState(logFilterInitialState);
const { filterQuery, filterQueryDraft } = state;
const setLogFilterQueryDraft = useMemo(() => {
const setDraft = (payload: KueryFilterQuery) =>
setState(prevState => ({ ...prevState, filterQueryDraft: payload }));
return (expression: string) =>
setDraft({
kind: 'kuery',
expression,
});
}, []);
const applyLogFilterQuery = useMemo(() => {
const applyQuery = (payload: SerializedFilterQuery) =>
setState(prevState => ({
...prevState,
filterQueryDraft: payload.query,
filterQuery: payload,
}));
return (expression: string) =>
applyQuery({
query: {
kind: 'kuery',
expression,
},
serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern),
});
}, [indexPattern]);
const isFilterQueryDraftValid = useMemo(() => {
if (filterQueryDraft?.kind === 'kuery') {
try {
esKuery.fromKueryExpression(filterQueryDraft.expression);
} catch (err) {
return false;
}
}
return true;
}, [filterQueryDraft]);
const serializedFilterQuery = useMemo(() => (filterQuery ? filterQuery.serializedQuery : null), [
filterQuery,
]);
return {
...state,
filterQueryAsKuery: state.filterQuery ? state.filterQuery.query : null,
filterQuery: serializedFilterQuery,
isFilterQueryDraftValid,
setLogFilterQueryDraft,
applyLogFilterQuery,
};
};
export const LogFilterState = createContainer(useLogFilterState);

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useContext } from 'react';
import { LogFilterState, LogFilterStateParams } from './log_filter_state';
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state';
type LogFilterUrlState = LogFilterStateParams['filterQueryAsKuery'];
export const WithLogFilterUrlState: React.FC = () => {
const { filterQueryAsKuery, applyLogFilterQuery } = useContext(LogFilterState.Context);
return (
<UrlStateContainer
urlState={filterQueryAsKuery}
urlStateKey="logFilter"
mapToUrlState={mapToFilterQuery}
onChange={urlState => {
if (urlState) {
applyLogFilterQuery(urlState.expression);
}
}}
onInitialize={urlState => {
if (urlState) {
applyLogFilterQuery(urlState.expression);
}
}}
/>
);
};
const mapToFilterQuery = (value: any): LogFilterUrlState | undefined =>
value?.kind === 'kuery' && typeof value.expression === 'string'
? {
kind: value.kind,
expression: value.expression,
}
: undefined;
export const replaceLogFilterInQueryString = (expression: string) =>
replaceStateKeyInQueryString<LogFilterUrlState>('logFilter', {
kind: 'kuery',
expression,
});

View file

@ -7,7 +7,6 @@
import React, { useEffect, useContext } from 'react';
import { TimeKey } from '../../../../common/time';
import { withLogFilter } from '../with_log_filter';
import { withLogPosition } from '../with_log_position';
import { LogHighlightsState } from './log_highlights';
@ -35,21 +34,8 @@ export const LogHighlightsPositionBridge = withLogPosition(
}
);
export const LogHighlightsFilterQueryBridge = withLogFilter(
({ serializedFilterQuery }: { serializedFilterQuery: string | null }) => {
const { setFilterQuery } = useContext(LogHighlightsState.Context);
useEffect(() => {
setFilterQuery(serializedFilterQuery);
}, [serializedFilterQuery, setFilterQuery]);
return null;
}
);
export const LogHighlightsBridge = ({ indexPattern }: { indexPattern: any }) => (
export const LogHighlightsBridge = () => (
<>
<LogHighlightsPositionBridge />
<LogHighlightsFilterQueryBridge indexPattern={indexPattern} />
</>
);

View file

@ -7,19 +7,18 @@
import { useContext } from 'react';
import { connect } from 'react-redux';
import { logFilterSelectors, logPositionSelectors, State } from '../../../store';
import { logPositionSelectors, State } from '../../../store';
import { RendererFunction } from '../../../utils/typed_react';
import { Source } from '../../source';
import { LogViewConfiguration } from '../log_view_configuration';
import { LogSummaryBuckets, useLogSummary } from './log_summary';
import { LogFilterState } from '../log_filter';
export const WithSummary = connect((state: State) => ({
visibleMidpointTime: logPositionSelectors.selectVisibleMidpointOrTargetTime(state),
filterQuery: logFilterSelectors.selectLogFilterQueryAsJson(state),
}))(
({
children,
filterQuery,
visibleMidpointTime,
}: {
children: RendererFunction<{
@ -27,11 +26,11 @@ export const WithSummary = connect((state: State) => ({
start: number | null;
end: number | null;
}>;
filterQuery: string | null;
visibleMidpointTime: number | null;
}) => {
const { intervalSize } = useContext(LogViewConfiguration.Context);
const { sourceId } = useContext(Source.Context);
const { filterQuery } = useContext(LogFilterState.Context);
const { buckets, start, end } = useLogSummary(
sourceId,

View file

@ -1,100 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { connect } from 'react-redux';
import { IIndexPattern } from 'src/plugins/data/public';
import { logFilterActions, logFilterSelectors, State } from '../../store';
import { FilterQuery } from '../../store/local/log_filter';
import { convertKueryToElasticSearchQuery } from '../../utils/kuery';
import { asChildFunctionRenderer } from '../../utils/typed_react';
import { bindPlainActionCreators } from '../../utils/typed_redux';
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../utils/url_state';
interface WithLogFilterProps {
indexPattern: IIndexPattern;
}
export const withLogFilter = connect(
(state: State) => ({
filterQuery: logFilterSelectors.selectLogFilterQuery(state),
serializedFilterQuery: logFilterSelectors.selectLogFilterQueryAsJson(state),
filterQueryDraft: logFilterSelectors.selectLogFilterQueryDraft(state),
isFilterQueryDraftValid: logFilterSelectors.selectIsLogFilterQueryDraftValid(state),
}),
(dispatch, ownProps: WithLogFilterProps) =>
bindPlainActionCreators({
applyFilterQuery: (query: FilterQuery) =>
logFilterActions.applyLogFilterQuery({
query,
serializedQuery: convertKueryToElasticSearchQuery(
query.expression,
ownProps.indexPattern
),
}),
applyFilterQueryFromKueryExpression: (expression: string) =>
logFilterActions.applyLogFilterQuery({
query: {
kind: 'kuery',
expression,
},
serializedQuery: convertKueryToElasticSearchQuery(expression, ownProps.indexPattern),
}),
setFilterQueryDraft: logFilterActions.setLogFilterQueryDraft,
setFilterQueryDraftFromKueryExpression: (expression: string) =>
logFilterActions.setLogFilterQueryDraft({
kind: 'kuery',
expression,
}),
})(dispatch)
);
export const WithLogFilter = asChildFunctionRenderer(withLogFilter);
/**
* Url State
*/
type LogFilterUrlState = ReturnType<typeof logFilterSelectors.selectLogFilterQuery>;
type WithLogFilterUrlStateProps = WithLogFilterProps;
export const WithLogFilterUrlState: React.FC<WithLogFilterUrlStateProps> = ({ indexPattern }) => (
<WithLogFilter indexPattern={indexPattern}>
{({ applyFilterQuery, filterQuery }) => (
<UrlStateContainer
urlState={filterQuery}
urlStateKey="logFilter"
mapToUrlState={mapToFilterQuery}
onChange={urlState => {
if (urlState) {
applyFilterQuery(urlState);
}
}}
onInitialize={urlState => {
if (urlState) {
applyFilterQuery(urlState);
}
}}
/>
)}
</WithLogFilter>
);
const mapToFilterQuery = (value: any): LogFilterUrlState | undefined =>
value && value.kind === 'kuery' && typeof value.expression === 'string'
? {
kind: value.kind,
expression: value.expression,
}
: undefined;
export const replaceLogFilterInQueryString = (expression: string) =>
replaceStateKeyInQueryString<LogFilterUrlState>('logFilter', {
kind: 'kuery',
expression,
});

View file

@ -8,7 +8,7 @@ import compose from 'lodash/fp/compose';
import React from 'react';
import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom';
import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter';
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
import { replaceSourceIdInQueryString } from '../../containers/source_id';
import { getFilterFromLocation, getTimeFromLocation } from './query_params';

View file

@ -11,7 +11,7 @@ import React from 'react';
import { Redirect, RouteComponentProps } from 'react-router-dom';
import { LoadingPage } from '../../components/loading_page';
import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter';
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
import { replaceSourceIdInQueryString } from '../../containers/source_id';
import { InfraNodeType, SourceConfigurationFields } from '../../graphql/types';

View file

@ -15,7 +15,7 @@ import { PageContent } from '../../../components/page';
import { WithSummary } from '../../../containers/logs/log_summary';
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
import { WithLogFilter, WithLogFilterUrlState } from '../../../containers/logs/with_log_filter';
import { LogFilterState } from '../../../containers/logs/log_filter';
import {
LogFlyout as LogFlyoutState,
WithFlyoutOptionsUrlState,
@ -31,7 +31,7 @@ import { LogsToolbar } from './page_toolbar';
import { LogHighlightsBridge, LogHighlightsState } from '../../../containers/logs/log_highlights';
export const LogsPageLogsContent: React.FunctionComponent = () => {
const { createDerivedIndexPattern, source, sourceId, version } = useContext(Source.Context);
const { source, sourceId, version } = useContext(Source.Context);
const { intervalSize, textScale, textWrap } = useContext(LogViewConfiguration.Context);
const {
setFlyoutVisibility,
@ -43,37 +43,32 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
isLoading,
} = useContext(LogFlyoutState.Context);
const { logSummaryHighlights } = useContext(LogHighlightsState.Context);
const derivedIndexPattern = createDerivedIndexPattern('logs');
const { applyLogFilterQuery } = useContext(LogFilterState.Context);
return (
<>
<LogHighlightsBridge indexPattern={derivedIndexPattern} />
<WithLogFilterUrlState indexPattern={derivedIndexPattern} />
<LogHighlightsBridge />
<WithLogPositionUrlState />
<WithLogMinimapUrlState />
<WithLogTextviewUrlState />
<WithFlyoutOptionsUrlState />
<LogsToolbar />
<WithLogFilter indexPattern={derivedIndexPattern}>
{({ applyFilterQueryFromKueryExpression }) => (
<WithLogPosition>
{({ jumpToTargetPosition, stopLiveStreaming }) =>
flyoutVisible ? (
<LogEntryFlyout
setFilter={applyFilterQueryFromKueryExpression}
setTarget={(timeKey, flyoutItemId) => {
jumpToTargetPosition(timeKey);
setSurroundingLogsId(flyoutItemId);
stopLiveStreaming();
}}
setFlyoutVisibility={setFlyoutVisibility}
flyoutItem={flyoutItem}
loading={isLoading}
/>
) : null
}
</WithLogPosition>
)}
</WithLogFilter>
<WithLogPosition>
{({ jumpToTargetPosition, stopLiveStreaming }) =>
flyoutVisible ? (
<LogEntryFlyout
setFilter={applyLogFilterQuery}
setTarget={(timeKey, flyoutItemId) => {
jumpToTargetPosition(timeKey);
setSurroundingLogsId(flyoutItemId);
stopLiveStreaming();
}}
setFlyoutVisibility={setFlyoutVisibility}
flyoutItem={flyoutItem}
loading={isLoading}
/>
) : null
}
</WithLogPosition>
<PageContent key={`${sourceId}-${version}`}>
<WithLogPosition>
{({
@ -132,12 +127,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
<WithSummary>
{({ buckets }) => (
<WithLogPosition>
{({
isAutoReloading,
jumpToTargetPosition,
visibleMidpointTime,
visibleTimeInterval,
}) => (
{({ jumpToTargetPosition, visibleMidpointTime, visibleTimeInterval }) => (
<WithStreamItems>
{({ isReloading }) => (
<LogMinimap

View file

@ -10,11 +10,22 @@ import { LogFlyout } from '../../../containers/logs/log_flyout';
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
import { LogHighlightsState } from '../../../containers/logs/log_highlights/log_highlights';
import { LogPositionState } from '../../../containers/logs/log_position';
import { LogFilterState } from '../../../containers/logs/log_filter';
import { LogFilterState, WithLogFilterUrlState } from '../../../containers/logs/log_filter';
import { LogEntriesState } from '../../../containers/logs/log_entries';
import { Source } from '../../../containers/source';
const LogFilterStateProvider: React.FC = ({ children }) => {
const { createDerivedIndexPattern } = useContext(Source.Context);
const derivedIndexPattern = createDerivedIndexPattern('logs');
return (
<LogFilterState.Provider indexPattern={derivedIndexPattern}>
<WithLogFilterUrlState />
{children}
</LogFilterState.Provider>
);
};
const LogEntriesStateProvider: React.FC = ({ children }) => {
const { sourceId } = useContext(Source.Context);
const { timeKey, pagesBeforeStart, pagesAfterEnd, isAutoReloading } = useContext(
@ -51,11 +62,11 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
<LogViewConfiguration.Provider>
<LogFlyout.Provider>
<LogPositionState.Provider>
<LogFilterState.Provider>
<LogFilterStateProvider>
<LogEntriesStateProvider>
<LogHighlightsStateProvider>{children}</LogHighlightsStateProvider>
</LogEntriesStateProvider>
</LogFilterState.Provider>
</LogFilterStateProvider>
</LogPositionState.Provider>
</LogFlyout.Provider>
</LogViewConfiguration.Provider>

View file

@ -19,7 +19,7 @@ import { LogTextWrapControls } from '../../../components/logging/log_text_wrap_c
import { LogTimeControls } from '../../../components/logging/log_time_controls';
import { LogFlyout } from '../../../containers/logs/log_flyout';
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
import { WithLogFilter } from '../../../containers/logs/with_log_filter';
import { LogFilterState } from '../../../containers/logs/log_filter';
import { WithLogPosition } from '../../../containers/logs/with_log_position';
import { Source } from '../../../containers/source';
import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion';
@ -37,7 +37,12 @@ export const LogsToolbar = () => {
textScale,
textWrap,
} = useContext(LogViewConfiguration.Context);
const {
filterQueryDraft,
isFilterQueryDraftValid,
applyLogFilterQuery,
setLogFilterQueryDraft,
} = useContext(LogFilterState.Context);
const { setSurroundingLogsId } = useContext(LogFlyout.Context);
const {
@ -55,38 +60,28 @@ export const LogsToolbar = () => {
<EuiFlexItem>
<WithKueryAutocompletion indexPattern={derivedIndexPattern}>
{({ isLoadingSuggestions, loadSuggestions, suggestions }) => (
<WithLogFilter indexPattern={derivedIndexPattern}>
{({
applyFilterQueryFromKueryExpression,
filterQueryDraft,
isFilterQueryDraftValid,
setFilterQueryDraftFromKueryExpression,
}) => (
<AutocompleteField
isLoadingSuggestions={isLoadingSuggestions}
isValid={isFilterQueryDraftValid}
loadSuggestions={loadSuggestions}
onChange={(expression: string) => {
setSurroundingLogsId(null);
setFilterQueryDraftFromKueryExpression(expression);
}}
onSubmit={(expression: string) => {
setSurroundingLogsId(null);
applyFilterQueryFromKueryExpression(expression);
}}
placeholder={i18n.translate(
'xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder',
{ defaultMessage: 'Search for log entries… (e.g. host.name:host-1)' }
)}
suggestions={suggestions}
value={filterQueryDraft ? filterQueryDraft.expression : ''}
aria-label={i18n.translate(
'xpack.infra.logsPage.toolbar.kqlSearchFieldAriaLabel',
{ defaultMessage: 'Search for log entries' }
)}
/>
<AutocompleteField
isLoadingSuggestions={isLoadingSuggestions}
isValid={isFilterQueryDraftValid}
loadSuggestions={loadSuggestions}
onChange={(expression: string) => {
setSurroundingLogsId(null);
setLogFilterQueryDraft(expression);
}}
onSubmit={(expression: string) => {
setSurroundingLogsId(null);
applyLogFilterQuery(expression);
}}
placeholder={i18n.translate(
'xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder',
{ defaultMessage: 'Search for log entries… (e.g. host.name:host-1)' }
)}
</WithLogFilter>
suggestions={suggestions}
value={filterQueryDraft ? filterQueryDraft.expression : ''}
aria-label={i18n.translate('xpack.infra.logsPage.toolbar.kqlSearchFieldAriaLabel', {
defaultMessage: 'Search for log entries',
})}
/>
)}
</WithKueryAutocompletion>
</EuiFlexItem>

View file

@ -5,7 +5,6 @@
*/
export {
logFilterActions,
logPositionActions,
waffleFilterActions,
waffleTimeActions,

View file

@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { logFilterActions } from './log_filter';
export { logPositionActions } from './log_position';
export { waffleFilterActions } from './waffle_filter';
export { waffleTimeActions } from './waffle_time';

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import actionCreatorFactory from 'typescript-fsa';
import { FilterQuery, SerializedFilterQuery } from './reducer';
const actionCreator = actionCreatorFactory('x-pack/infra/local/log_filter');
export const setLogFilterQueryDraft = actionCreator<FilterQuery>('SET_LOG_FILTER_QUERY_DRAFT');
export const applyLogFilterQuery = actionCreator<SerializedFilterQuery>('APPLY_LOG_FILTER_QUERY');

View file

@ -1,11 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as logFilterActions from './actions';
import * as logFilterSelectors from './selectors';
export { logFilterActions, logFilterSelectors };
export * from './reducer';

View file

@ -1,43 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { reducerWithInitialState } from 'typescript-fsa-reducers/dist';
import { applyLogFilterQuery, setLogFilterQueryDraft } from './actions';
export interface KueryFilterQuery {
kind: 'kuery';
expression: string;
}
export type FilterQuery = KueryFilterQuery;
export interface SerializedFilterQuery {
query: FilterQuery;
serializedQuery: string;
}
export interface LogFilterState {
filterQuery: SerializedFilterQuery | null;
filterQueryDraft: KueryFilterQuery | null;
}
export const initialLogFilterState: LogFilterState = {
filterQuery: null,
filterQueryDraft: null,
};
export const logFilterReducer = reducerWithInitialState(initialLogFilterState)
.case(setLogFilterQueryDraft, (state, filterQueryDraft) => ({
...state,
filterQueryDraft,
}))
.case(applyLogFilterQuery, (state, filterQuery) => ({
...state,
filterQuery,
filterQueryDraft: filterQuery.query,
}))
.build();

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { createSelector } from 'reselect';
import { LogFilterState } from './reducer';
import { esKuery } from '../../../../../../../../src/plugins/data/public';
export const selectLogFilterQuery = (state: LogFilterState) =>
state.filterQuery ? state.filterQuery.query : null;
export const selectLogFilterQueryAsJson = (state: LogFilterState) =>
state.filterQuery ? state.filterQuery.serializedQuery : null;
export const selectLogFilterQueryDraft = (state: LogFilterState) => state.filterQueryDraft;
export const selectIsLogFilterQueryDraftValid = createSelector(
selectLogFilterQueryDraft,
filterQueryDraft => {
if (filterQueryDraft && filterQueryDraft.kind === 'kuery') {
try {
esKuery.fromKueryExpression(filterQueryDraft.expression);
} catch (err) {
return false;
}
}
return true;
}
);

View file

@ -6,7 +6,6 @@
import { combineReducers } from 'redux';
import { initialLogFilterState, logFilterReducer, LogFilterState } from './log_filter';
import { initialLogPositionState, logPositionReducer, LogPositionState } from './log_position';
import { initialWaffleFilterState, waffleFilterReducer, WaffleFilterState } from './waffle_filter';
import {
@ -17,7 +16,6 @@ import {
import { initialWaffleTimeState, waffleTimeReducer, WaffleTimeState } from './waffle_time';
export interface LocalState {
logFilter: LogFilterState;
logPosition: LogPositionState;
waffleFilter: WaffleFilterState;
waffleTime: WaffleTimeState;
@ -25,7 +23,6 @@ export interface LocalState {
}
export const initialLocalState: LocalState = {
logFilter: initialLogFilterState,
logPosition: initialLogPositionState,
waffleFilter: initialWaffleFilterState,
waffleTime: initialWaffleTimeState,
@ -33,7 +30,6 @@ export const initialLocalState: LocalState = {
};
export const localReducer = combineReducers<LocalState>({
logFilter: logFilterReducer,
logPosition: logPositionReducer,
waffleFilter: waffleFilterReducer,
waffleTime: waffleTimeReducer,

View file

@ -5,18 +5,12 @@
*/
import { globalizeSelectors } from '../../utils/typed_redux';
import { logFilterSelectors as innerLogFilterSelectors } from './log_filter';
import { logPositionSelectors as innerLogPositionSelectors } from './log_position';
import { LocalState } from './reducer';
import { waffleFilterSelectors as innerWaffleFilterSelectors } from './waffle_filter';
import { waffleOptionsSelectors as innerWaffleOptionsSelectors } from './waffle_options';
import { waffleTimeSelectors as innerWaffleTimeSelectors } from './waffle_time';
export const logFilterSelectors = globalizeSelectors(
(state: LocalState) => state.logFilter,
innerLogFilterSelectors
);
export const logPositionSelectors = globalizeSelectors(
(state: LocalState) => state.logPosition,
innerLogPositionSelectors

View file

@ -6,7 +6,6 @@
import { globalizeSelectors } from '../utils/typed_redux';
import {
logFilterSelectors as localLogFilterSelectors,
logPositionSelectors as localLogPositionSelectors,
waffleFilterSelectors as localWaffleFilterSelectors,
waffleOptionsSelectors as localWaffleOptionsSelectors,
@ -19,7 +18,6 @@ import { State } from './reducer';
const selectLocal = (state: State) => state.local;
export const logFilterSelectors = globalizeSelectors(selectLocal, localLogFilterSelectors);
export const logPositionSelectors = globalizeSelectors(selectLocal, localLogPositionSelectors);
export const waffleFilterSelectors = globalizeSelectors(selectLocal, localWaffleFilterSelectors);
export const waffleTimeSelectors = globalizeSelectors(selectLocal, localWaffleTimeSelectors);

View file

@ -12,7 +12,6 @@ import { map } from 'rxjs/operators';
import {
createRootEpic,
initialState,
logFilterSelectors,
logPositionSelectors,
reducer,
State,
@ -39,7 +38,6 @@ export function createStore({ apolloClient, observableApi }: StoreDependencies)
apolloClient$: apolloClient,
selectIsAutoReloadingLogEntries: logPositionSelectors.selectIsAutoReloading,
selectIsAutoReloadingScrollLocked: logPositionSelectors.selectAutoReloadScrollLock,
selectLogFilterQueryAsJson: logFilterSelectors.selectLogFilterQueryAsJson,
selectLogTargetPosition: logPositionSelectors.selectTargetPosition,
selectVisibleLogMidpointOrTarget: logPositionSelectors.selectVisibleMidpointOrTarget,
selectWaffleTimeUpdatePolicyInterval: waffleTimeSelectors.selectTimeUpdatePolicyInterval,

View file

@ -23,7 +23,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
};
const expectedSearchString =
"logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194))&sourceId=default";
const expectedRedirect = `/logs/stream?${expectedSearchString}`;
const expectedRedirectPath = '/logs/stream?';
await pageObjects.common.navigateToActualUrl(
'infraOps',
@ -32,7 +32,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await retry.tryForTime(5000, async () => {
const currentUrl = await browser.getCurrentUrl();
const [, currentHash] = decodeURIComponent(currentUrl).split('#');
expect(currentHash).to.contain(expectedRedirect);
// Account for unpredictable location of the g parameter in the search string
expect(currentHash.slice(0, expectedRedirectPath.length)).to.be(expectedRedirectPath);
expect(currentHash.slice(expectedRedirectPath.length)).to.contain(expectedSearchString);
});
});
});