mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[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 commit43302b354a
. * Revert "Move URL state to hook" This reverts commitc61f5b190b
. Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
ace50d8d2b
commit
a03f395b52
22 changed files with 223 additions and 328 deletions
|
@ -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';
|
||||
|
|
|
@ -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);
|
|
@ -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,
|
||||
});
|
|
@ -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} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
});
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
export {
|
||||
logFilterActions,
|
||||
logPositionActions,
|
||||
waffleFilterActions,
|
||||
waffleTimeActions,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
|
@ -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';
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
||||
);
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue