[Logs UI] Refactor log position to hooks (#53540)
* Move URL state to hook
* Fix log filter URL state infinite loop
* Initial refactor of log position to hooks
* Simplify and reimplement controlsShouldDisplayTargetPosition
* Fix live streaming
* Flatten logposition destructuring
* Revert "Move URL state to hook"
This reverts commit 4e04aa061d
.
# Conflicts:
# x-pack/legacy/plugins/infra/public/containers/logs/log_filter/use_log_filter_url_state.tsx
# x-pack/legacy/plugins/infra/public/pages/logs/stream/page_providers.tsx
* Fix unused imports
* Fix link-to test
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
b7a534b1b1
commit
f5448bd9f4
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { transparentize } from 'polished';
|
||||
|
||||
import euiStyled from '../../../../../../common/eui_styled_components';
|
||||
|
@ -21,28 +21,26 @@ import {
|
|||
LogEntryColumnWidths,
|
||||
} from './log_entry_column';
|
||||
import { ASSUMED_SCROLLBAR_WIDTH } from './vertical_scroll_panel';
|
||||
import { WithLogPosition } from '../../../containers/logs/with_log_position';
|
||||
import { LogPositionState } from '../../../containers/logs/log_position';
|
||||
import { localizedDate } from '../../../utils/formatters/datetime';
|
||||
|
||||
export const LogColumnHeaders: React.FunctionComponent<{
|
||||
columnConfigurations: LogColumnConfiguration[];
|
||||
columnWidths: LogEntryColumnWidths;
|
||||
}> = ({ columnConfigurations, columnWidths }) => {
|
||||
const { firstVisiblePosition } = useContext(LogPositionState.Context);
|
||||
return (
|
||||
<LogColumnHeadersWrapper>
|
||||
{columnConfigurations.map(columnConfiguration => {
|
||||
if (isTimestampLogColumnConfiguration(columnConfiguration)) {
|
||||
return (
|
||||
<WithLogPosition key={columnConfiguration.timestampColumn.id}>
|
||||
{({ firstVisiblePosition }) => (
|
||||
<LogColumnHeader
|
||||
columnWidth={columnWidths[columnConfiguration.timestampColumn.id]}
|
||||
data-test-subj="logColumnHeader timestampLogColumnHeader"
|
||||
>
|
||||
{firstVisiblePosition ? localizedDate(firstVisiblePosition.time) : 'Timestamp'}
|
||||
</LogColumnHeader>
|
||||
)}
|
||||
</WithLogPosition>
|
||||
<LogColumnHeader
|
||||
key={columnConfiguration.timestampColumn.id}
|
||||
columnWidth={columnWidths[columnConfiguration.timestampColumn.id]}
|
||||
data-test-subj="logColumnHeader timestampLogColumnHeader"
|
||||
>
|
||||
{firstVisiblePosition ? localizedDate(firstVisiblePosition.time) : 'Timestamp'}
|
||||
</LogColumnHeader>
|
||||
);
|
||||
} else if (isMessageLogColumnConfiguration(columnConfiguration)) {
|
||||
return (
|
||||
|
|
|
@ -55,17 +55,13 @@ interface ScrollableLogTextStreamViewProps {
|
|||
setFlyoutVisibility: (visible: boolean) => void;
|
||||
highlightedItem: string | null;
|
||||
currentHighlightKey: UniqueTimeKey | null;
|
||||
scrollLock: {
|
||||
enable: () => void;
|
||||
disable: () => void;
|
||||
isEnabled: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface ScrollableLogTextStreamViewState {
|
||||
target: TimeKey | null;
|
||||
targetId: string | null;
|
||||
items: StreamItem[];
|
||||
isScrollLocked: boolean;
|
||||
}
|
||||
|
||||
export class ScrollableLogTextStreamView extends React.PureComponent<
|
||||
|
@ -81,8 +77,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
|
||||
// Prevent new entries from being appended and moving the stream forward when
|
||||
// the user has scrolled up during live streaming
|
||||
const nextItems =
|
||||
hasItems && nextProps.scrollLock.isEnabled ? prevState.items : nextProps.items;
|
||||
const nextItems = hasItems && prevState.isScrollLocked ? prevState.items : nextProps.items;
|
||||
|
||||
if (nextProps.isStreaming && hasItems) {
|
||||
return {
|
||||
|
@ -121,6 +116,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
target: null,
|
||||
targetId: null,
|
||||
items: props.items,
|
||||
isScrollLocked: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -137,9 +133,8 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
lastLoadedTime,
|
||||
scale,
|
||||
wrap,
|
||||
scrollLock,
|
||||
} = this.props;
|
||||
const { targetId, items } = this.state;
|
||||
const { targetId, items, isScrollLocked } = this.state;
|
||||
const hasItems = items.length > 0;
|
||||
return (
|
||||
<ScrollableLogTextStreamViewWrapper>
|
||||
|
@ -187,7 +182,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
target={targetId}
|
||||
hideScrollbar={true}
|
||||
data-test-subj={'logStream'}
|
||||
isLocked={scrollLock.isEnabled}
|
||||
isLocked={isScrollLocked}
|
||||
entriesCount={items.length}
|
||||
>
|
||||
{registerChild => (
|
||||
|
@ -248,7 +243,7 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
lastStreamingUpdate={isStreaming ? lastLoadedTime : null}
|
||||
onLoadMore={this.handleLoadNewerItems}
|
||||
/>
|
||||
{scrollLock.isEnabled && (
|
||||
{isScrollLocked && (
|
||||
<LogTextStreamJumpToTail
|
||||
width={width}
|
||||
onClickJump={this.handleJumpToTail}
|
||||
|
@ -308,7 +303,9 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
fromScroll: boolean;
|
||||
}) => {
|
||||
if (fromScroll && this.props.isStreaming) {
|
||||
this.props.scrollLock[pagesBelow === 0 ? 'disable' : 'enable']();
|
||||
this.setState({
|
||||
isScrollLocked: pagesBelow !== 0,
|
||||
});
|
||||
}
|
||||
this.props.reportVisibleInterval({
|
||||
endKey: parseStreamItemId(bottomChild),
|
||||
|
@ -322,11 +319,11 @@ export class ScrollableLogTextStreamView extends React.PureComponent<
|
|||
);
|
||||
|
||||
private handleJumpToTail = () => {
|
||||
const { items, scrollLock } = this.props;
|
||||
scrollLock.disable();
|
||||
const { items } = this.props;
|
||||
const lastItemTarget = getStreamItemId(items[items.length - 1]);
|
||||
this.setState({
|
||||
targetId: lastItemTarget,
|
||||
isScrollLocked: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { useEffect, useState, useReducer, useCallback } from 'react';
|
||||
import createContainer from 'constate';
|
||||
import { pick, throttle } from 'lodash';
|
||||
import { pick, throttle, omit } from 'lodash';
|
||||
import { useGraphQLQueries } from './gql_queries';
|
||||
import { TimeKey, timeKeyIsBetween } from '../../../../common/time';
|
||||
import { InfraLogEntry } from './types';
|
||||
|
@ -45,6 +45,7 @@ interface LogEntriesProps {
|
|||
pagesAfterEnd: number | null;
|
||||
sourceId: string;
|
||||
isAutoReloading: boolean;
|
||||
jumpToTargetPosition: (position: TimeKey) => void;
|
||||
}
|
||||
|
||||
type FetchEntriesParams = Omit<LogEntriesProps, 'isAutoReloading'>;
|
||||
|
@ -65,7 +66,7 @@ export type LogEntriesStateParams = {
|
|||
} & LogEntriesResponse;
|
||||
|
||||
export interface LogEntriesCallbacks {
|
||||
fetchNewerEntries: () => Promise<void>;
|
||||
fetchNewerEntries: () => Promise<TimeKey | null | undefined>;
|
||||
}
|
||||
export const logEntriesInitialCallbacks = {
|
||||
fetchNewerEntries: async () => {},
|
||||
|
@ -127,10 +128,13 @@ const useFetchEntriesEffect = (
|
|||
const [prevParams, cachePrevParams] = useState(props);
|
||||
const [startedStreaming, setStartedStreaming] = useState(false);
|
||||
|
||||
const runFetchNewEntriesRequest = async () => {
|
||||
const runFetchNewEntriesRequest = async (override = {}) => {
|
||||
dispatch({ type: Action.FetchingNewEntries });
|
||||
try {
|
||||
const payload = await getLogEntriesAround(props);
|
||||
const payload = await getLogEntriesAround({
|
||||
...omit(props, 'jumpToTargetPosition'),
|
||||
...override,
|
||||
});
|
||||
dispatch({ type: Action.ReceiveNewEntries, payload });
|
||||
} catch (e) {
|
||||
dispatch({ type: Action.ErrorOnNewEntries });
|
||||
|
@ -150,6 +154,7 @@ const useFetchEntriesEffect = (
|
|||
type: getEntriesBefore ? Action.ReceiveEntriesBefore : Action.ReceiveEntriesAfter,
|
||||
payload,
|
||||
});
|
||||
return payload.entriesEnd;
|
||||
} catch (e) {
|
||||
dispatch({ type: Action.ErrorOnMoreEntries });
|
||||
}
|
||||
|
@ -185,19 +190,37 @@ const useFetchEntriesEffect = (
|
|||
|
||||
const fetchNewerEntries = useCallback(
|
||||
throttle(() => runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After), 500),
|
||||
[props]
|
||||
[props, state.entriesEnd]
|
||||
);
|
||||
|
||||
const streamEntriesEffectDependencies = [props.isAutoReloading, state.isLoadingMore];
|
||||
const streamEntriesEffectDependencies = [
|
||||
props.isAutoReloading,
|
||||
state.isLoadingMore,
|
||||
state.isReloading,
|
||||
];
|
||||
const streamEntriesEffect = () => {
|
||||
(async () => {
|
||||
if (props.isAutoReloading && !state.isLoadingMore) {
|
||||
if (props.isAutoReloading && !state.isLoadingMore && !state.isReloading) {
|
||||
if (startedStreaming) {
|
||||
await new Promise(res => setTimeout(res, 5000));
|
||||
} else {
|
||||
const nowKey = {
|
||||
tiebreaker: 0,
|
||||
time: Date.now(),
|
||||
};
|
||||
props.jumpToTargetPosition(nowKey);
|
||||
setStartedStreaming(true);
|
||||
if (state.hasMoreAfterEnd) {
|
||||
runFetchNewEntriesRequest({
|
||||
timeKey: nowKey,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const newEntriesEnd = await runFetchMoreEntriesRequest(ShouldFetchMoreEntries.After);
|
||||
if (newEntriesEnd) {
|
||||
props.jumpToTargetPosition(newEntriesEnd);
|
||||
}
|
||||
fetchNewerEntries();
|
||||
} else if (!props.isAutoReloading) {
|
||||
setStartedStreaming(false);
|
||||
}
|
||||
|
|
|
@ -5,4 +5,3 @@
|
|||
*/
|
||||
|
||||
export * from './log_highlights';
|
||||
export * from './redux_bridges';
|
||||
|
|
|
@ -9,9 +9,9 @@ import { useState, useContext } from 'react';
|
|||
import { useLogEntryHighlights } from './log_entry_highlights';
|
||||
import { useLogSummaryHighlights } from './log_summary_highlights';
|
||||
import { useNextAndPrevious } from './next_and_previous';
|
||||
import { useReduxBridgeSetters } from './redux_bridge_setters';
|
||||
import { useLogSummaryBufferInterval } from '../log_summary';
|
||||
import { LogViewConfiguration } from '../log_view_configuration';
|
||||
import { LogPositionState } from '../log_position';
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
|
||||
export const useLogHighlightsState = ({
|
||||
|
@ -28,14 +28,7 @@ export const useLogHighlightsState = ({
|
|||
filterQuery: string | null;
|
||||
}) => {
|
||||
const [highlightTerms, setHighlightTerms] = useState<string[]>([]);
|
||||
const {
|
||||
visibleMidpoint,
|
||||
setFilterQuery,
|
||||
setVisibleMidpoint,
|
||||
jumpToTarget,
|
||||
setJumpToTarget,
|
||||
} = useReduxBridgeSetters();
|
||||
|
||||
const { visibleMidpoint, jumpToTargetPosition } = useContext(LogPositionState.Context);
|
||||
const { intervalSize: summaryIntervalSize } = useContext(LogViewConfiguration.Context);
|
||||
const {
|
||||
start: summaryStart,
|
||||
|
@ -79,25 +72,22 @@ export const useLogHighlightsState = ({
|
|||
visibleMidpoint,
|
||||
logEntryHighlights,
|
||||
highlightTerms,
|
||||
jumpToTarget,
|
||||
jumpToTargetPosition,
|
||||
});
|
||||
|
||||
return {
|
||||
highlightTerms,
|
||||
setHighlightTerms,
|
||||
setFilterQuery,
|
||||
logEntryHighlights,
|
||||
logEntryHighlightsById,
|
||||
logSummaryHighlights,
|
||||
loadLogEntryHighlightsRequest,
|
||||
loadLogSummaryHighlightsRequest,
|
||||
setVisibleMidpoint,
|
||||
currentHighlightKey,
|
||||
hasPreviousHighlight,
|
||||
hasNextHighlight,
|
||||
goToPreviousHighlight,
|
||||
goToNextHighlight,
|
||||
setJumpToTarget,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@ import { LogEntryHighlights } from './log_entry_highlights';
|
|||
|
||||
export const useNextAndPrevious = ({
|
||||
highlightTerms,
|
||||
jumpToTarget,
|
||||
jumpToTargetPosition,
|
||||
logEntryHighlights,
|
||||
visibleMidpoint,
|
||||
}: {
|
||||
highlightTerms: string[];
|
||||
jumpToTarget: (target: TimeKey) => void;
|
||||
jumpToTargetPosition: (target: TimeKey) => void;
|
||||
logEntryHighlights: LogEntryHighlights | undefined;
|
||||
visibleMidpoint: TimeKey | null;
|
||||
}) => {
|
||||
|
@ -41,9 +41,9 @@ export const useNextAndPrevious = ({
|
|||
|
||||
useEffect(() => {
|
||||
if (currentTimeKey) {
|
||||
jumpToTarget(currentTimeKey);
|
||||
jumpToTargetPosition(currentTimeKey);
|
||||
}
|
||||
}, [currentTimeKey, jumpToTarget]);
|
||||
}, [currentTimeKey, jumpToTargetPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTimeKey === null && entries.length > 0) {
|
||||
|
|
|
@ -1,23 +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 { useState } from 'react';
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
|
||||
export const useReduxBridgeSetters = () => {
|
||||
const [filterQuery, setFilterQuery] = useState<string | null>(null);
|
||||
const [visibleMidpoint, setVisibleMidpoint] = useState<TimeKey | null>(null);
|
||||
const [jumpToTarget, setJumpToTarget] = useState<(target: TimeKey) => void>(() => undefined);
|
||||
|
||||
return {
|
||||
filterQuery,
|
||||
visibleMidpoint,
|
||||
setFilterQuery,
|
||||
setVisibleMidpoint,
|
||||
jumpToTarget,
|
||||
setJumpToTarget,
|
||||
};
|
||||
};
|
|
@ -1,41 +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, { useEffect, useContext } from 'react';
|
||||
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
import { withLogPosition } from '../with_log_position';
|
||||
import { LogHighlightsState } from './log_highlights';
|
||||
|
||||
// Bridges Redux container state with Hooks state. Once state is moved fully from
|
||||
// Redux to Hooks this can be removed.
|
||||
|
||||
export const LogHighlightsPositionBridge = withLogPosition(
|
||||
({
|
||||
visibleMidpoint,
|
||||
jumpToTargetPosition,
|
||||
}: {
|
||||
visibleMidpoint: TimeKey | null;
|
||||
jumpToTargetPosition: (target: TimeKey) => void;
|
||||
}) => {
|
||||
const { setJumpToTarget, setVisibleMidpoint } = useContext(LogHighlightsState.Context);
|
||||
useEffect(() => {
|
||||
setVisibleMidpoint(visibleMidpoint);
|
||||
}, [setVisibleMidpoint, visibleMidpoint]);
|
||||
|
||||
useEffect(() => {
|
||||
setJumpToTarget(() => jumpToTargetPosition);
|
||||
}, [jumpToTargetPosition, setJumpToTarget]);
|
||||
|
||||
return null;
|
||||
}
|
||||
);
|
||||
|
||||
export const LogHighlightsBridge = () => (
|
||||
<>
|
||||
<LogHighlightsPositionBridge />
|
||||
</>
|
||||
);
|
|
@ -4,32 +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 { logPositionSelectors as logPositionReduxSelectors } from '../../../store/local/selectors';
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
|
||||
export const useLogPositionState = () => {
|
||||
const { local: state } = useContext(ReduxStateContext);
|
||||
const timeKey = logPositionReduxSelectors.selectVisibleMidpointOrTarget(state);
|
||||
const pages = logPositionReduxSelectors.selectPagesBeforeAndAfter(state);
|
||||
const isAutoReloading = logPositionReduxSelectors.selectIsAutoReloading(state);
|
||||
return { timeKey, isAutoReloading, ...pages };
|
||||
};
|
||||
|
||||
export interface LogPositionStateParams {
|
||||
timeKey: TimeKey | null;
|
||||
pagesAfterEnd: number | null;
|
||||
pagesBeforeStart: number | null;
|
||||
isAutoReloading: boolean;
|
||||
}
|
||||
|
||||
export const logPositionInitialState = {
|
||||
timeKey: null,
|
||||
pagesAfterEnd: null,
|
||||
pagesBeforeStart: null,
|
||||
isAutoReloading: false,
|
||||
};
|
||||
|
||||
export const LogPositionState = createContainer(useLogPositionState);
|
||||
export * from './log_position_state';
|
||||
export * from './with_log_position_url_state';
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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, useEffect, useCallback } from 'react';
|
||||
import createContainer from 'constate';
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
|
||||
type TimeKeyOrNull = TimeKey | null;
|
||||
|
||||
interface VisiblePositions {
|
||||
startKey: TimeKeyOrNull;
|
||||
middleKey: TimeKeyOrNull;
|
||||
endKey: TimeKeyOrNull;
|
||||
pagesAfterEnd: number;
|
||||
pagesBeforeStart: number;
|
||||
}
|
||||
|
||||
export interface LogPositionStateParams {
|
||||
targetPosition: TimeKeyOrNull;
|
||||
isAutoReloading: boolean;
|
||||
firstVisiblePosition: TimeKeyOrNull;
|
||||
pagesBeforeStart: number;
|
||||
pagesAfterEnd: number;
|
||||
visibleMidpoint: TimeKeyOrNull;
|
||||
visibleMidpointTime: number | null;
|
||||
visibleTimeInterval: { start: number; end: number } | null;
|
||||
}
|
||||
|
||||
export interface LogPositionCallbacks {
|
||||
jumpToTargetPosition: (pos: TimeKeyOrNull) => void;
|
||||
jumpToTargetPositionTime: (time: number) => void;
|
||||
reportVisiblePositions: (visPos: VisiblePositions) => void;
|
||||
startLiveStreaming: () => void;
|
||||
stopLiveStreaming: () => void;
|
||||
}
|
||||
|
||||
const useVisibleMidpoint = (middleKey: TimeKeyOrNull, targetPosition: TimeKeyOrNull) => {
|
||||
// Of the two dependencies `middleKey` and `targetPosition`, return
|
||||
// whichever one was the most recently updated. This allows the UI controls
|
||||
// to display a newly-selected `targetPosition` before loading new data;
|
||||
// otherwise the previous `middleKey` would linger in the UI for the entirety
|
||||
// of the loading operation, which the user could perceive as unresponsiveness
|
||||
const [store, update] = useState({
|
||||
middleKey,
|
||||
targetPosition,
|
||||
currentValue: middleKey || targetPosition,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (middleKey !== store.middleKey) {
|
||||
update({ targetPosition, middleKey, currentValue: middleKey });
|
||||
} else if (targetPosition !== store.targetPosition) {
|
||||
update({ targetPosition, middleKey, currentValue: targetPosition });
|
||||
}
|
||||
}, [middleKey, targetPosition]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return store.currentValue;
|
||||
};
|
||||
|
||||
export const useLogPositionState: () => LogPositionStateParams & LogPositionCallbacks = () => {
|
||||
const [targetPosition, jumpToTargetPosition] = useState<TimeKey | null>(null);
|
||||
const [isAutoReloading, setIsAutoReloading] = useState(false);
|
||||
const [visiblePositions, reportVisiblePositions] = useState<VisiblePositions>({
|
||||
endKey: null,
|
||||
middleKey: null,
|
||||
startKey: null,
|
||||
pagesBeforeStart: Infinity,
|
||||
pagesAfterEnd: Infinity,
|
||||
});
|
||||
|
||||
const { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd } = visiblePositions;
|
||||
|
||||
const visibleMidpoint = useVisibleMidpoint(middleKey, targetPosition);
|
||||
|
||||
const visibleTimeInterval = useMemo(
|
||||
() => (startKey && endKey ? { start: startKey.time, end: endKey.time } : null),
|
||||
[startKey, endKey]
|
||||
);
|
||||
|
||||
const state = {
|
||||
targetPosition,
|
||||
isAutoReloading,
|
||||
firstVisiblePosition: startKey,
|
||||
pagesBeforeStart,
|
||||
pagesAfterEnd,
|
||||
visibleMidpoint,
|
||||
visibleMidpointTime: visibleMidpoint ? visibleMidpoint.time : null,
|
||||
visibleTimeInterval,
|
||||
};
|
||||
|
||||
const callbacks = {
|
||||
jumpToTargetPosition,
|
||||
jumpToTargetPositionTime: useCallback(
|
||||
(time: number) => jumpToTargetPosition({ tiebreaker: 0, time }),
|
||||
[jumpToTargetPosition]
|
||||
),
|
||||
reportVisiblePositions,
|
||||
startLiveStreaming: useCallback(() => setIsAutoReloading(true), [setIsAutoReloading]),
|
||||
stopLiveStreaming: useCallback(() => setIsAutoReloading(false), [setIsAutoReloading]),
|
||||
};
|
||||
|
||||
return { ...state, ...callbacks };
|
||||
};
|
||||
|
||||
export const LogPositionState = createContainer(useLogPositionState);
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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, useMemo } from 'react';
|
||||
|
||||
import { pickTimeKey } from '../../../../common/time';
|
||||
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state';
|
||||
import { LogPositionState, LogPositionStateParams } from './log_position_state';
|
||||
|
||||
/**
|
||||
* Url State
|
||||
*/
|
||||
|
||||
interface LogPositionUrlState {
|
||||
position: LogPositionStateParams['visibleMidpoint'] | undefined;
|
||||
streamLive?: boolean | undefined;
|
||||
}
|
||||
|
||||
export const WithLogPositionUrlState = () => {
|
||||
const {
|
||||
visibleMidpoint,
|
||||
isAutoReloading,
|
||||
jumpToTargetPosition,
|
||||
jumpToTargetPositionTime,
|
||||
startLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
} = useContext(LogPositionState.Context);
|
||||
const urlState = useMemo(
|
||||
() => ({
|
||||
position: visibleMidpoint ? pickTimeKey(visibleMidpoint) : null,
|
||||
streamLive: isAutoReloading,
|
||||
}),
|
||||
[visibleMidpoint, isAutoReloading]
|
||||
);
|
||||
return (
|
||||
<UrlStateContainer
|
||||
urlState={urlState}
|
||||
urlStateKey="logPosition"
|
||||
mapToUrlState={mapToUrlState}
|
||||
onChange={(newUrlState: LogPositionUrlState | undefined) => {
|
||||
if (newUrlState && newUrlState.position) {
|
||||
jumpToTargetPosition(newUrlState.position);
|
||||
}
|
||||
if (newUrlState && newUrlState.streamLive) {
|
||||
startLiveStreaming();
|
||||
} else if (
|
||||
newUrlState &&
|
||||
typeof newUrlState.streamLive !== 'undefined' &&
|
||||
!newUrlState.streamLive
|
||||
) {
|
||||
stopLiveStreaming();
|
||||
}
|
||||
}}
|
||||
onInitialize={(initialUrlState: LogPositionUrlState | undefined) => {
|
||||
if (initialUrlState && initialUrlState.position) {
|
||||
jumpToTargetPosition(initialUrlState.position);
|
||||
} else {
|
||||
jumpToTargetPositionTime(Date.now());
|
||||
}
|
||||
if (initialUrlState && initialUrlState.streamLive) {
|
||||
startLiveStreaming();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const mapToUrlState = (value: any): LogPositionUrlState | undefined =>
|
||||
value
|
||||
? {
|
||||
position: mapToPositionUrlState(value.position),
|
||||
streamLive: mapToStreamLiveUrlState(value.streamLive),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const mapToPositionUrlState = (value: any) =>
|
||||
value && typeof value.time === 'number' && typeof value.tiebreaker === 'number'
|
||||
? pickTimeKey(value)
|
||||
: undefined;
|
||||
|
||||
const mapToStreamLiveUrlState = (value: any) => (typeof value === 'boolean' ? value : undefined);
|
||||
|
||||
export const replaceLogPositionInQueryString = (time: number) =>
|
||||
Number.isNaN(time)
|
||||
? (value: string) => value
|
||||
: replaceStateKeyInQueryString<LogPositionUrlState>('logPosition', {
|
||||
position: {
|
||||
time,
|
||||
tiebreaker: 0,
|
||||
},
|
||||
});
|
|
@ -5,40 +5,34 @@
|
|||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
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';
|
||||
import { LogPositionState } from '../log_position';
|
||||
|
||||
export const WithSummary = connect((state: State) => ({
|
||||
visibleMidpointTime: logPositionSelectors.selectVisibleMidpointOrTargetTime(state),
|
||||
}))(
|
||||
({
|
||||
children,
|
||||
export const WithSummary = ({
|
||||
children,
|
||||
}: {
|
||||
children: RendererFunction<{
|
||||
buckets: LogSummaryBuckets;
|
||||
start: number | null;
|
||||
end: number | null;
|
||||
}>;
|
||||
}) => {
|
||||
const { intervalSize } = useContext(LogViewConfiguration.Context);
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const { filterQuery } = useContext(LogFilterState.Context);
|
||||
const { visibleMidpointTime } = useContext(LogPositionState.Context);
|
||||
|
||||
const { buckets, start, end } = useLogSummary(
|
||||
sourceId,
|
||||
visibleMidpointTime,
|
||||
}: {
|
||||
children: RendererFunction<{
|
||||
buckets: LogSummaryBuckets;
|
||||
start: number | null;
|
||||
end: number | null;
|
||||
}>;
|
||||
visibleMidpointTime: number | null;
|
||||
}) => {
|
||||
const { intervalSize } = useContext(LogViewConfiguration.Context);
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const { filterQuery } = useContext(LogFilterState.Context);
|
||||
intervalSize,
|
||||
filterQuery
|
||||
);
|
||||
|
||||
const { buckets, start, end } = useLogSummary(
|
||||
sourceId,
|
||||
visibleMidpointTime,
|
||||
intervalSize,
|
||||
filterQuery
|
||||
);
|
||||
|
||||
return children({ buckets, start, end });
|
||||
}
|
||||
);
|
||||
return children({ buckets, start, end });
|
||||
};
|
||||
|
|
|
@ -1,127 +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 { createSelector } from 'reselect';
|
||||
|
||||
import { pickTimeKey } from '../../../common/time';
|
||||
import { logPositionActions, logPositionSelectors, State } from '../../store';
|
||||
import { asChildFunctionRenderer } from '../../utils/typed_react';
|
||||
import { bindPlainActionCreators } from '../../utils/typed_redux';
|
||||
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../utils/url_state';
|
||||
|
||||
export const withLogPosition = connect(
|
||||
(state: State) => ({
|
||||
firstVisiblePosition: logPositionSelectors.selectFirstVisiblePosition(state),
|
||||
isAutoReloading: logPositionSelectors.selectIsAutoReloading(state),
|
||||
isScrollLocked: logPositionSelectors.selectAutoReloadScrollLock(state),
|
||||
lastVisiblePosition: logPositionSelectors.selectFirstVisiblePosition(state),
|
||||
targetPosition: logPositionSelectors.selectTargetPosition(state),
|
||||
urlState: selectPositionUrlState(state),
|
||||
visibleTimeInterval: logPositionSelectors.selectVisibleTimeInterval(state),
|
||||
visibleMidpoint: logPositionSelectors.selectVisibleMidpointOrTarget(state),
|
||||
visibleMidpointTime: logPositionSelectors.selectVisibleMidpointOrTargetTime(state),
|
||||
}),
|
||||
bindPlainActionCreators({
|
||||
jumpToTargetPosition: logPositionActions.jumpToTargetPosition,
|
||||
jumpToTargetPositionTime: logPositionActions.jumpToTargetPositionTime,
|
||||
reportVisiblePositions: logPositionActions.reportVisiblePositions,
|
||||
startLiveStreaming: logPositionActions.startAutoReload,
|
||||
stopLiveStreaming: logPositionActions.stopAutoReload,
|
||||
scrollLockLiveStreaming: logPositionActions.lockAutoReloadScroll,
|
||||
scrollUnlockLiveStreaming: logPositionActions.unlockAutoReloadScroll,
|
||||
})
|
||||
);
|
||||
|
||||
export const WithLogPosition = asChildFunctionRenderer(withLogPosition, {
|
||||
onCleanup: ({ stopLiveStreaming }) => stopLiveStreaming(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Url State
|
||||
*/
|
||||
|
||||
interface LogPositionUrlState {
|
||||
position?: ReturnType<typeof logPositionSelectors.selectVisibleMidpointOrTarget>;
|
||||
streamLive?: ReturnType<typeof logPositionSelectors.selectIsAutoReloading>;
|
||||
}
|
||||
|
||||
export const WithLogPositionUrlState = () => (
|
||||
<WithLogPosition>
|
||||
{({
|
||||
jumpToTargetPosition,
|
||||
jumpToTargetPositionTime,
|
||||
startLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
urlState,
|
||||
}) => (
|
||||
<UrlStateContainer
|
||||
urlState={urlState}
|
||||
urlStateKey="logPosition"
|
||||
mapToUrlState={mapToUrlState}
|
||||
onChange={newUrlState => {
|
||||
if (newUrlState && newUrlState.position) {
|
||||
jumpToTargetPosition(newUrlState.position);
|
||||
}
|
||||
if (newUrlState && newUrlState.streamLive) {
|
||||
startLiveStreaming();
|
||||
} else if (
|
||||
newUrlState &&
|
||||
typeof newUrlState.streamLive !== 'undefined' &&
|
||||
!newUrlState.streamLive
|
||||
) {
|
||||
stopLiveStreaming();
|
||||
}
|
||||
}}
|
||||
onInitialize={initialUrlState => {
|
||||
if (initialUrlState && initialUrlState.position) {
|
||||
jumpToTargetPosition(initialUrlState.position);
|
||||
} else {
|
||||
jumpToTargetPositionTime(Date.now());
|
||||
}
|
||||
if (initialUrlState && initialUrlState.streamLive) {
|
||||
startLiveStreaming();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</WithLogPosition>
|
||||
);
|
||||
|
||||
const selectPositionUrlState = createSelector(
|
||||
logPositionSelectors.selectVisibleMidpointOrTarget,
|
||||
logPositionSelectors.selectIsAutoReloading,
|
||||
(position, streamLive) => ({
|
||||
position: position ? pickTimeKey(position) : null,
|
||||
streamLive,
|
||||
})
|
||||
);
|
||||
|
||||
const mapToUrlState = (value: any): LogPositionUrlState | undefined =>
|
||||
value
|
||||
? {
|
||||
position: mapToPositionUrlState(value.position),
|
||||
streamLive: mapToStreamLiveUrlState(value.streamLive),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const mapToPositionUrlState = (value: any) =>
|
||||
value && typeof value.time === 'number' && typeof value.tiebreaker === 'number'
|
||||
? pickTimeKey(value)
|
||||
: undefined;
|
||||
|
||||
const mapToStreamLiveUrlState = (value: any) => (typeof value === 'boolean' ? value : undefined);
|
||||
|
||||
export const replaceLogPositionInQueryString = (time: number) =>
|
||||
Number.isNaN(time)
|
||||
? (value: string) => value
|
||||
: replaceStateKeyInQueryString<LogPositionUrlState>('logPosition', {
|
||||
position: {
|
||||
time,
|
||||
tiebreaker: 0,
|
||||
},
|
||||
});
|
|
@ -1,35 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Temporary Workaround
|
||||
* This is not a well-designed container. It only exists to enable quick
|
||||
* migration of the redux-based logging ui into the infra-ui codebase. It will
|
||||
* be removed during the refactoring to graphql/apollo.
|
||||
*/
|
||||
import { connect } from 'react-redux';
|
||||
import { bindPlainActionCreators } from '../../utils/typed_redux';
|
||||
|
||||
import {
|
||||
// searchActions,
|
||||
// searchResultsSelectors,
|
||||
// sharedSelectors,
|
||||
logPositionActions,
|
||||
State,
|
||||
} from '../../store';
|
||||
|
||||
export const withLogSearchControlsProps = connect(
|
||||
(state: State) => ({
|
||||
// isLoadingSearchResults: searchResultsSelectors.selectIsLoadingSearchResults(state),
|
||||
// nextSearchResult: sharedSelectors.selectNextSearchResultKey(state),
|
||||
// previousSearchResult: sharedSelectors.selectPreviousSearchResultKey(state),
|
||||
}),
|
||||
bindPlainActionCreators({
|
||||
// clearSearch: searchActions.clearSearch,
|
||||
jumpToTarget: logPositionActions.jumpToTargetPosition,
|
||||
// search: searchActions.search,
|
||||
})
|
||||
);
|
|
@ -10,7 +10,6 @@ import { LogEntry, LogEntryHighlight } from '../../utils/log_entry';
|
|||
import { RendererFunction } from '../../utils/typed_react';
|
||||
// deep inporting to avoid a circular import problem
|
||||
import { LogHighlightsState } from './log_highlights/log_highlights';
|
||||
import { LogPositionState } from './log_position';
|
||||
import { LogEntriesState, LogEntriesStateParams, LogEntriesCallbacks } from './log_entries';
|
||||
import { UniqueTimeKey } from '../../../common/time';
|
||||
|
||||
|
@ -24,18 +23,17 @@ export const WithStreamItems: React.FunctionComponent<{
|
|||
>;
|
||||
}> = ({ children }) => {
|
||||
const [logEntries, logEntriesCallbacks] = useContext(LogEntriesState.Context);
|
||||
const { isAutoReloading } = useContext(LogPositionState.Context);
|
||||
const { currentHighlightKey, logEntryHighlightsById } = useContext(LogHighlightsState.Context);
|
||||
|
||||
const items = useMemo(
|
||||
() =>
|
||||
logEntries.isReloading && !isAutoReloading
|
||||
logEntries.isReloading
|
||||
? []
|
||||
: logEntries.entries.map(logEntry =>
|
||||
createLogEntryStreamItem(logEntry, logEntryHighlightsById[logEntry.gid] || [])
|
||||
),
|
||||
|
||||
[isAutoReloading, logEntries.entries, logEntries.isReloading, logEntryHighlightsById]
|
||||
[logEntries.entries, logEntries.isReloading, logEntryHighlightsById]
|
||||
);
|
||||
|
||||
return children({
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
|
||||
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
|
||||
import { replaceLogPositionInQueryString } from '../../containers/logs/log_position';
|
||||
import { replaceSourceIdInQueryString } from '../../containers/source_id';
|
||||
import { getFilterFromLocation, getTimeFromLocation } from './query_params';
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Redirect, RouteComponentProps } from 'react-router-dom';
|
|||
|
||||
import { LoadingPage } from '../../components/loading_page';
|
||||
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
|
||||
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
|
||||
import { replaceLogPositionInQueryString } from '../../containers/logs/log_position';
|
||||
import { replaceSourceIdInQueryString } from '../../containers/source_id';
|
||||
import { InfraNodeType, SourceConfigurationFields } from '../../graphql/types';
|
||||
import { getFilterFromLocation, getTimeFromLocation } from './query_params';
|
||||
|
|
|
@ -21,14 +21,13 @@ import {
|
|||
WithFlyoutOptionsUrlState,
|
||||
} from '../../../containers/logs/log_flyout';
|
||||
import { WithLogMinimapUrlState } from '../../../containers/logs/with_log_minimap';
|
||||
import { WithLogPositionUrlState } from '../../../containers/logs/with_log_position';
|
||||
import { WithLogPosition } from '../../../containers/logs/with_log_position';
|
||||
import { LogPositionState } from '../../../containers/logs/log_position';
|
||||
import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview';
|
||||
import { WithStreamItems } from '../../../containers/logs/with_stream_items';
|
||||
import { Source } from '../../../containers/source';
|
||||
|
||||
import { LogsToolbar } from './page_toolbar';
|
||||
import { LogHighlightsBridge, LogHighlightsState } from '../../../containers/logs/log_highlights';
|
||||
import { LogHighlightsState } from '../../../containers/logs/log_highlights';
|
||||
|
||||
export const LogsPageLogsContent: React.FunctionComponent = () => {
|
||||
const { source, sourceId, version } = useContext(Source.Context);
|
||||
|
@ -44,110 +43,91 @@ export const LogsPageLogsContent: React.FunctionComponent = () => {
|
|||
} = useContext(LogFlyoutState.Context);
|
||||
const { logSummaryHighlights } = useContext(LogHighlightsState.Context);
|
||||
const { applyLogFilterQuery } = useContext(LogFilterState.Context);
|
||||
const {
|
||||
isAutoReloading,
|
||||
targetPosition,
|
||||
visibleMidpointTime,
|
||||
visibleTimeInterval,
|
||||
reportVisiblePositions,
|
||||
jumpToTargetPosition,
|
||||
stopLiveStreaming,
|
||||
} = useContext(LogPositionState.Context);
|
||||
return (
|
||||
<>
|
||||
<LogHighlightsBridge />
|
||||
<WithLogPositionUrlState />
|
||||
<WithLogMinimapUrlState />
|
||||
<WithLogTextviewUrlState />
|
||||
<WithFlyoutOptionsUrlState />
|
||||
<LogsToolbar />
|
||||
<WithLogPosition>
|
||||
{({ jumpToTargetPosition, stopLiveStreaming }) =>
|
||||
flyoutVisible ? (
|
||||
<LogEntryFlyout
|
||||
setFilter={applyLogFilterQuery}
|
||||
setTarget={(timeKey, flyoutItemId) => {
|
||||
jumpToTargetPosition(timeKey);
|
||||
setSurroundingLogsId(flyoutItemId);
|
||||
stopLiveStreaming();
|
||||
}}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
flyoutItem={flyoutItem}
|
||||
loading={isLoading}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
</WithLogPosition>
|
||||
{flyoutVisible ? (
|
||||
<LogEntryFlyout
|
||||
setFilter={applyLogFilterQuery}
|
||||
setTarget={(timeKey, flyoutItemId) => {
|
||||
jumpToTargetPosition(timeKey);
|
||||
setSurroundingLogsId(flyoutItemId);
|
||||
stopLiveStreaming();
|
||||
}}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
flyoutItem={flyoutItem}
|
||||
loading={isLoading}
|
||||
/>
|
||||
) : null}
|
||||
<PageContent key={`${sourceId}-${version}`}>
|
||||
<WithLogPosition>
|
||||
<WithStreamItems>
|
||||
{({
|
||||
isAutoReloading,
|
||||
jumpToTargetPosition,
|
||||
reportVisiblePositions,
|
||||
targetPosition,
|
||||
scrollLockLiveStreaming,
|
||||
scrollUnlockLiveStreaming,
|
||||
isScrollLocked,
|
||||
currentHighlightKey,
|
||||
hasMoreAfterEnd,
|
||||
hasMoreBeforeStart,
|
||||
isLoadingMore,
|
||||
isReloading,
|
||||
items,
|
||||
lastLoadedTime,
|
||||
fetchNewerEntries,
|
||||
}) => (
|
||||
<WithStreamItems>
|
||||
{({
|
||||
currentHighlightKey,
|
||||
hasMoreAfterEnd,
|
||||
hasMoreBeforeStart,
|
||||
isLoadingMore,
|
||||
isReloading,
|
||||
items,
|
||||
lastLoadedTime,
|
||||
fetchNewerEntries,
|
||||
}) => (
|
||||
<ScrollableLogTextStreamView
|
||||
columnConfigurations={(source && source.configuration.logColumns) || []}
|
||||
hasMoreAfterEnd={hasMoreAfterEnd}
|
||||
hasMoreBeforeStart={hasMoreBeforeStart}
|
||||
isLoadingMore={isLoadingMore}
|
||||
isReloading={isReloading}
|
||||
isStreaming={isAutoReloading}
|
||||
items={items}
|
||||
jumpToTarget={jumpToTargetPosition}
|
||||
lastLoadedTime={lastLoadedTime}
|
||||
loadNewerItems={fetchNewerEntries}
|
||||
reportVisibleInterval={reportVisiblePositions}
|
||||
scale={textScale}
|
||||
target={targetPosition}
|
||||
wrap={textWrap}
|
||||
setFlyoutItem={setFlyoutId}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
highlightedItem={surroundingLogsId ? surroundingLogsId : null}
|
||||
currentHighlightKey={currentHighlightKey}
|
||||
scrollLock={{
|
||||
enable: scrollLockLiveStreaming,
|
||||
disable: scrollUnlockLiveStreaming,
|
||||
isEnabled: isScrollLocked,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</WithStreamItems>
|
||||
<ScrollableLogTextStreamView
|
||||
columnConfigurations={(source && source.configuration.logColumns) || []}
|
||||
hasMoreAfterEnd={hasMoreAfterEnd}
|
||||
hasMoreBeforeStart={hasMoreBeforeStart}
|
||||
isLoadingMore={isLoadingMore}
|
||||
isReloading={isReloading}
|
||||
isStreaming={isAutoReloading}
|
||||
items={items}
|
||||
jumpToTarget={jumpToTargetPosition}
|
||||
lastLoadedTime={lastLoadedTime}
|
||||
loadNewerItems={fetchNewerEntries}
|
||||
reportVisibleInterval={reportVisiblePositions}
|
||||
scale={textScale}
|
||||
target={targetPosition}
|
||||
wrap={textWrap}
|
||||
setFlyoutItem={setFlyoutId}
|
||||
setFlyoutVisibility={setFlyoutVisibility}
|
||||
highlightedItem={surroundingLogsId ? surroundingLogsId : null}
|
||||
currentHighlightKey={currentHighlightKey}
|
||||
/>
|
||||
)}
|
||||
</WithLogPosition>
|
||||
</WithStreamItems>
|
||||
|
||||
<AutoSizer content bounds detectAnyWindowResize="height">
|
||||
{({ measureRef, bounds: { height = 0 }, content: { width = 0 } }) => {
|
||||
return (
|
||||
<LogPageMinimapColumn ref={measureRef}>
|
||||
<WithSummary>
|
||||
{({ buckets }) => (
|
||||
<WithLogPosition>
|
||||
{({ jumpToTargetPosition, visibleMidpointTime, visibleTimeInterval }) => (
|
||||
<WithStreamItems>
|
||||
{({ isReloading }) => (
|
||||
<LogMinimap
|
||||
height={height}
|
||||
width={width}
|
||||
highlightedInterval={isReloading ? null : visibleTimeInterval}
|
||||
intervalSize={intervalSize}
|
||||
jumpToTarget={jumpToTargetPosition}
|
||||
summaryBuckets={buckets}
|
||||
summaryHighlightBuckets={
|
||||
logSummaryHighlights.length > 0
|
||||
? logSummaryHighlights[0].buckets
|
||||
: []
|
||||
}
|
||||
target={visibleMidpointTime}
|
||||
/>
|
||||
)}
|
||||
</WithStreamItems>
|
||||
<WithStreamItems>
|
||||
{({ isReloading }) => (
|
||||
<LogMinimap
|
||||
height={height}
|
||||
width={width}
|
||||
highlightedInterval={isReloading ? null : visibleTimeInterval}
|
||||
intervalSize={intervalSize}
|
||||
jumpToTarget={jumpToTargetPosition}
|
||||
summaryBuckets={buckets}
|
||||
summaryHighlightBuckets={
|
||||
logSummaryHighlights.length > 0 ? logSummaryHighlights[0].buckets : []
|
||||
}
|
||||
target={visibleMidpointTime}
|
||||
/>
|
||||
)}
|
||||
</WithLogPosition>
|
||||
</WithStreamItems>
|
||||
)}
|
||||
</WithSummary>
|
||||
</LogPageMinimapColumn>
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { useContext } from 'react';
|
|||
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 { LogPositionState, WithLogPositionUrlState } from '../../../containers/logs/log_position';
|
||||
import { LogFilterState, WithLogFilterUrlState } from '../../../containers/logs/log_filter';
|
||||
import { LogEntriesState } from '../../../containers/logs/log_entries';
|
||||
|
||||
|
@ -28,17 +28,23 @@ const LogFilterStateProvider: React.FC = ({ children }) => {
|
|||
|
||||
const LogEntriesStateProvider: React.FC = ({ children }) => {
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const { timeKey, pagesBeforeStart, pagesAfterEnd, isAutoReloading } = useContext(
|
||||
LogPositionState.Context
|
||||
);
|
||||
const {
|
||||
targetPosition,
|
||||
pagesBeforeStart,
|
||||
pagesAfterEnd,
|
||||
isAutoReloading,
|
||||
jumpToTargetPosition,
|
||||
} = useContext(LogPositionState.Context);
|
||||
const { filterQuery } = useContext(LogFilterState.Context);
|
||||
|
||||
const entriesProps = {
|
||||
timeKey,
|
||||
timeKey: targetPosition,
|
||||
pagesBeforeStart,
|
||||
pagesAfterEnd,
|
||||
filterQuery,
|
||||
sourceId,
|
||||
isAutoReloading,
|
||||
jumpToTargetPosition,
|
||||
};
|
||||
return <LogEntriesState.Provider {...entriesProps}>{children}</LogEntriesState.Provider>;
|
||||
};
|
||||
|
@ -62,6 +68,7 @@ export const LogsPageProviders: React.FunctionComponent = ({ children }) => {
|
|||
<LogViewConfiguration.Provider>
|
||||
<LogFlyout.Provider>
|
||||
<LogPositionState.Provider>
|
||||
<WithLogPositionUrlState />
|
||||
<LogFilterStateProvider>
|
||||
<LogEntriesStateProvider>
|
||||
<LogHighlightsStateProvider>{children}</LogHighlightsStateProvider>
|
||||
|
|
|
@ -20,7 +20,7 @@ import { LogTimeControls } from '../../../components/logging/log_time_controls';
|
|||
import { LogFlyout } from '../../../containers/logs/log_flyout';
|
||||
import { LogViewConfiguration } from '../../../containers/logs/log_view_configuration';
|
||||
import { LogFilterState } from '../../../containers/logs/log_filter';
|
||||
import { WithLogPosition } from '../../../containers/logs/with_log_position';
|
||||
import { LogPositionState } from '../../../containers/logs/log_position';
|
||||
import { Source } from '../../../containers/source';
|
||||
import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion';
|
||||
|
||||
|
@ -54,6 +54,13 @@ export const LogsToolbar = () => {
|
|||
goToPreviousHighlight,
|
||||
goToNextHighlight,
|
||||
} = useContext(LogHighlightsState.Context);
|
||||
const {
|
||||
visibleMidpointTime,
|
||||
isAutoReloading,
|
||||
jumpToTargetPositionTime,
|
||||
startLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
} = useContext(LogPositionState.Context);
|
||||
return (
|
||||
<Toolbar>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="s">
|
||||
|
@ -114,26 +121,16 @@ export const LogsToolbar = () => {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<WithLogPosition resetOnUnmount>
|
||||
{({
|
||||
visibleMidpointTime,
|
||||
isAutoReloading,
|
||||
jumpToTargetPositionTime,
|
||||
startLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
}) => (
|
||||
<LogTimeControls
|
||||
currentTime={visibleMidpointTime}
|
||||
isLiveStreaming={isAutoReloading}
|
||||
jumpToTime={jumpToTargetPositionTime}
|
||||
startLiveStreaming={() => {
|
||||
startLiveStreaming();
|
||||
setSurroundingLogsId(null);
|
||||
}}
|
||||
stopLiveStreaming={stopLiveStreaming}
|
||||
/>
|
||||
)}
|
||||
</WithLogPosition>
|
||||
<LogTimeControls
|
||||
currentTime={visibleMidpointTime}
|
||||
isLiveStreaming={isAutoReloading}
|
||||
jumpToTime={jumpToTargetPositionTime}
|
||||
startLiveStreaming={() => {
|
||||
startLiveStreaming();
|
||||
setSurroundingLogsId(null);
|
||||
}}
|
||||
stopLiveStreaming={stopLiveStreaming}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</Toolbar>
|
||||
|
|
|
@ -4,9 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export {
|
||||
logPositionActions,
|
||||
waffleFilterActions,
|
||||
waffleTimeActions,
|
||||
waffleOptionsActions,
|
||||
} from './local';
|
||||
export { waffleFilterActions, waffleTimeActions, waffleOptionsActions } from './local';
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { logPositionActions } from './log_position';
|
||||
export { waffleFilterActions } from './waffle_filter';
|
||||
export { waffleTimeActions } from './waffle_time';
|
||||
export { waffleOptionsActions } from './waffle_options';
|
||||
|
|
|
@ -1,39 +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 { TimeKey } from '../../../../common/time';
|
||||
|
||||
const actionCreator = actionCreatorFactory('x-pack/infra/local/log_position');
|
||||
|
||||
export const jumpToTargetPosition = actionCreator<TimeKey>('JUMP_TO_TARGET_POSITION');
|
||||
|
||||
export const jumpToTargetPositionTime = (time: number, fromAutoReload: boolean = false) =>
|
||||
jumpToTargetPosition({
|
||||
tiebreaker: 0,
|
||||
time,
|
||||
fromAutoReload,
|
||||
});
|
||||
|
||||
export interface ReportVisiblePositionsPayload {
|
||||
pagesAfterEnd: number;
|
||||
pagesBeforeStart: number;
|
||||
endKey: TimeKey | null;
|
||||
middleKey: TimeKey | null;
|
||||
startKey: TimeKey | null;
|
||||
fromScroll: boolean;
|
||||
}
|
||||
|
||||
export const reportVisiblePositions = actionCreator<ReportVisiblePositionsPayload>(
|
||||
'REPORT_VISIBLE_POSITIONS'
|
||||
);
|
||||
|
||||
export const startAutoReload = actionCreator('START_AUTO_RELOAD');
|
||||
export const stopAutoReload = actionCreator('STOP_AUTO_RELOAD');
|
||||
|
||||
export const lockAutoReloadScroll = actionCreator('LOCK_AUTO_RELOAD_SCROLL');
|
||||
export const unlockAutoReloadScroll = actionCreator('UNLOCK_AUTO_RELOAD_SCROLL');
|
|
@ -1,53 +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 { Action } from 'redux';
|
||||
import { Epic, combineEpics } from 'redux-observable';
|
||||
import { timer } from 'rxjs';
|
||||
import { exhaustMap, filter, map, takeUntil, mapTo, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
jumpToTargetPosition,
|
||||
jumpToTargetPositionTime,
|
||||
startAutoReload,
|
||||
stopAutoReload,
|
||||
} from './actions';
|
||||
|
||||
const LIVE_STREAM_INTERVAL = 5000;
|
||||
|
||||
const createLiveStreamEpic = <State>(): Epic<
|
||||
Action,
|
||||
Action,
|
||||
State,
|
||||
{ selectIsAutoReloadingScrollLocked: (state: State) => boolean }
|
||||
> => (action$, state$, { selectIsAutoReloadingScrollLocked }) =>
|
||||
action$.pipe(
|
||||
filter(startAutoReload.match),
|
||||
exhaustMap(() =>
|
||||
timer(0, LIVE_STREAM_INTERVAL).pipe(
|
||||
withLatestFrom(state$),
|
||||
filter(([, state]) => selectIsAutoReloadingScrollLocked(state) === false),
|
||||
map(() => jumpToTargetPositionTime(Date.now(), true)),
|
||||
takeUntil(action$.pipe(filter(stopAutoReload.match)))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const createLiveStreamScrollCancelEpic = <State>(): Epic<
|
||||
Action,
|
||||
Action,
|
||||
State,
|
||||
{ selectIsAutoReloadingLogEntries: (state: State) => boolean }
|
||||
> => (action$, state$, { selectIsAutoReloadingLogEntries }) =>
|
||||
action$.pipe(
|
||||
filter(action => jumpToTargetPosition.match(action) && !action.payload.fromAutoReload),
|
||||
withLatestFrom(state$),
|
||||
filter(([, state]) => selectIsAutoReloadingLogEntries(state)),
|
||||
mapTo(stopAutoReload())
|
||||
);
|
||||
|
||||
export const createLogPositionEpic = <State>() =>
|
||||
combineEpics(createLiveStreamEpic<State>(), createLiveStreamScrollCancelEpic<State>());
|
|
@ -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 logPositionActions from './actions';
|
||||
import * as logPositionSelectors from './selectors';
|
||||
|
||||
export { logPositionActions, logPositionSelectors };
|
||||
export * from './reducer';
|
|
@ -1,120 +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 { combineReducers } from 'redux';
|
||||
import { reducerWithInitialState } from 'typescript-fsa-reducers/dist';
|
||||
|
||||
import { TimeKey } from '../../../../common/time';
|
||||
import {
|
||||
jumpToTargetPosition,
|
||||
reportVisiblePositions,
|
||||
startAutoReload,
|
||||
stopAutoReload,
|
||||
lockAutoReloadScroll,
|
||||
unlockAutoReloadScroll,
|
||||
} from './actions';
|
||||
|
||||
interface ManualTargetPositionUpdatePolicy {
|
||||
policy: 'manual';
|
||||
}
|
||||
|
||||
interface IntervalTargetPositionUpdatePolicy {
|
||||
policy: 'interval';
|
||||
}
|
||||
|
||||
type TargetPositionUpdatePolicy =
|
||||
| ManualTargetPositionUpdatePolicy
|
||||
| IntervalTargetPositionUpdatePolicy;
|
||||
|
||||
export interface LogPositionState {
|
||||
targetPosition: TimeKey | null;
|
||||
updatePolicy: TargetPositionUpdatePolicy;
|
||||
visiblePositions: {
|
||||
startKey: TimeKey | null;
|
||||
middleKey: TimeKey | null;
|
||||
endKey: TimeKey | null;
|
||||
pagesAfterEnd: number;
|
||||
pagesBeforeStart: number;
|
||||
};
|
||||
controlsShouldDisplayTargetPosition: boolean;
|
||||
autoReloadScrollLock: boolean;
|
||||
}
|
||||
|
||||
export const initialLogPositionState: LogPositionState = {
|
||||
targetPosition: null,
|
||||
updatePolicy: {
|
||||
policy: 'manual',
|
||||
},
|
||||
visiblePositions: {
|
||||
endKey: null,
|
||||
middleKey: null,
|
||||
startKey: null,
|
||||
pagesBeforeStart: Infinity,
|
||||
pagesAfterEnd: Infinity,
|
||||
},
|
||||
controlsShouldDisplayTargetPosition: false,
|
||||
autoReloadScrollLock: false,
|
||||
};
|
||||
|
||||
const targetPositionReducer = reducerWithInitialState(initialLogPositionState.targetPosition).case(
|
||||
jumpToTargetPosition,
|
||||
(state, target) => target
|
||||
);
|
||||
|
||||
const targetPositionUpdatePolicyReducer = reducerWithInitialState(
|
||||
initialLogPositionState.updatePolicy
|
||||
)
|
||||
.case(startAutoReload, () => ({
|
||||
policy: 'interval',
|
||||
}))
|
||||
.case(stopAutoReload, () => ({
|
||||
policy: 'manual',
|
||||
}));
|
||||
|
||||
const visiblePositionReducer = reducerWithInitialState(
|
||||
initialLogPositionState.visiblePositions
|
||||
).case(
|
||||
reportVisiblePositions,
|
||||
(state, { startKey, middleKey, endKey, pagesBeforeStart, pagesAfterEnd }) => ({
|
||||
endKey,
|
||||
middleKey,
|
||||
startKey,
|
||||
pagesBeforeStart,
|
||||
pagesAfterEnd,
|
||||
})
|
||||
);
|
||||
|
||||
// Determines whether to use the target position or the visible midpoint when
|
||||
// displaying a timestamp or time range in the toolbar and log minimap. When the
|
||||
// user jumps to a new target, the final visible midpoint is indeterminate until
|
||||
// all the new data has finished loading, so using this flag reduces the perception
|
||||
// that the UI is jumping around inaccurately
|
||||
const controlsShouldDisplayTargetPositionReducer = reducerWithInitialState(
|
||||
initialLogPositionState.controlsShouldDisplayTargetPosition
|
||||
)
|
||||
.case(jumpToTargetPosition, () => true)
|
||||
.case(stopAutoReload, () => false)
|
||||
.case(startAutoReload, () => true)
|
||||
.case(reportVisiblePositions, (state, { fromScroll }) => {
|
||||
if (fromScroll) return false;
|
||||
return state;
|
||||
});
|
||||
|
||||
const autoReloadScrollLockReducer = reducerWithInitialState(
|
||||
initialLogPositionState.autoReloadScrollLock
|
||||
)
|
||||
.case(startAutoReload, () => false)
|
||||
.case(stopAutoReload, () => false)
|
||||
.case(lockAutoReloadScroll, () => true)
|
||||
.case(unlockAutoReloadScroll, () => false);
|
||||
|
||||
export const logPositionReducer = combineReducers<LogPositionState>({
|
||||
targetPosition: targetPositionReducer,
|
||||
updatePolicy: targetPositionUpdatePolicyReducer,
|
||||
visiblePositions: visiblePositionReducer,
|
||||
controlsShouldDisplayTargetPosition: controlsShouldDisplayTargetPositionReducer,
|
||||
autoReloadScrollLock: autoReloadScrollLockReducer,
|
||||
});
|
|
@ -1,69 +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 { LogPositionState } from './reducer';
|
||||
|
||||
export const selectTargetPosition = (state: LogPositionState) => state.targetPosition;
|
||||
|
||||
export const selectIsAutoReloading = (state: LogPositionState) =>
|
||||
state.updatePolicy.policy === 'interval';
|
||||
|
||||
export const selectAutoReloadScrollLock = (state: LogPositionState) => state.autoReloadScrollLock;
|
||||
|
||||
export const selectFirstVisiblePosition = (state: LogPositionState) =>
|
||||
state.visiblePositions.startKey ? state.visiblePositions.startKey : null;
|
||||
|
||||
export const selectMiddleVisiblePosition = (state: LogPositionState) =>
|
||||
state.visiblePositions.middleKey ? state.visiblePositions.middleKey : null;
|
||||
|
||||
export const selectLastVisiblePosition = (state: LogPositionState) =>
|
||||
state.visiblePositions.endKey ? state.visiblePositions.endKey : null;
|
||||
|
||||
export const selectPagesBeforeAndAfter = (state: LogPositionState) =>
|
||||
state.visiblePositions
|
||||
? {
|
||||
pagesBeforeStart: state.visiblePositions.pagesBeforeStart,
|
||||
pagesAfterEnd: state.visiblePositions.pagesAfterEnd,
|
||||
}
|
||||
: { pagesBeforeStart: null, pagesAfterEnd: null };
|
||||
export const selectControlsShouldDisplayTargetPosition = (state: LogPositionState) =>
|
||||
state.controlsShouldDisplayTargetPosition;
|
||||
|
||||
export const selectVisibleMidpointOrTarget = createSelector(
|
||||
selectMiddleVisiblePosition,
|
||||
selectTargetPosition,
|
||||
selectControlsShouldDisplayTargetPosition,
|
||||
(middleVisiblePosition, targetPosition, displayTargetPosition) => {
|
||||
if (displayTargetPosition) {
|
||||
return targetPosition;
|
||||
} else if (middleVisiblePosition) {
|
||||
return middleVisiblePosition;
|
||||
} else if (targetPosition) {
|
||||
return targetPosition;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const selectVisibleMidpointOrTargetTime = createSelector(
|
||||
selectVisibleMidpointOrTarget,
|
||||
visibleMidpointOrTarget => (visibleMidpointOrTarget ? visibleMidpointOrTarget.time : null)
|
||||
);
|
||||
|
||||
export const selectVisibleTimeInterval = createSelector(
|
||||
selectFirstVisiblePosition,
|
||||
selectLastVisiblePosition,
|
||||
(firstVisiblePosition, lastVisiblePosition) =>
|
||||
firstVisiblePosition && lastVisiblePosition
|
||||
? {
|
||||
start: firstVisiblePosition.time,
|
||||
end: lastVisiblePosition.time,
|
||||
}
|
||||
: null
|
||||
);
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { combineReducers } from 'redux';
|
||||
|
||||
import { initialLogPositionState, logPositionReducer, LogPositionState } from './log_position';
|
||||
import { initialWaffleFilterState, waffleFilterReducer, WaffleFilterState } from './waffle_filter';
|
||||
import {
|
||||
initialWaffleOptionsState,
|
||||
|
@ -16,21 +15,18 @@ import {
|
|||
import { initialWaffleTimeState, waffleTimeReducer, WaffleTimeState } from './waffle_time';
|
||||
|
||||
export interface LocalState {
|
||||
logPosition: LogPositionState;
|
||||
waffleFilter: WaffleFilterState;
|
||||
waffleTime: WaffleTimeState;
|
||||
waffleMetrics: WaffleOptionsState;
|
||||
}
|
||||
|
||||
export const initialLocalState: LocalState = {
|
||||
logPosition: initialLogPositionState,
|
||||
waffleFilter: initialWaffleFilterState,
|
||||
waffleTime: initialWaffleTimeState,
|
||||
waffleMetrics: initialWaffleOptionsState,
|
||||
};
|
||||
|
||||
export const localReducer = combineReducers<LocalState>({
|
||||
logPosition: logPositionReducer,
|
||||
waffleFilter: waffleFilterReducer,
|
||||
waffleTime: waffleTimeReducer,
|
||||
waffleMetrics: waffleOptionsReducer,
|
||||
|
|
|
@ -5,17 +5,11 @@
|
|||
*/
|
||||
|
||||
import { globalizeSelectors } from '../../utils/typed_redux';
|
||||
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 logPositionSelectors = globalizeSelectors(
|
||||
(state: LocalState) => state.logPosition,
|
||||
innerLogPositionSelectors
|
||||
);
|
||||
|
||||
export const waffleFilterSelectors = globalizeSelectors(
|
||||
(state: LocalState) => state.waffleFilter,
|
||||
innerWaffleFilterSelectors
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { globalizeSelectors } from '../utils/typed_redux';
|
||||
import {
|
||||
logPositionSelectors as localLogPositionSelectors,
|
||||
waffleFilterSelectors as localWaffleFilterSelectors,
|
||||
waffleOptionsSelectors as localWaffleOptionsSelectors,
|
||||
waffleTimeSelectors as localWaffleTimeSelectors,
|
||||
|
@ -18,7 +17,6 @@ import { State } from './reducer';
|
|||
|
||||
const selectLocal = (state: State) => state.local;
|
||||
|
||||
export const logPositionSelectors = globalizeSelectors(selectLocal, localLogPositionSelectors);
|
||||
export const waffleFilterSelectors = globalizeSelectors(selectLocal, localWaffleFilterSelectors);
|
||||
export const waffleTimeSelectors = globalizeSelectors(selectLocal, localWaffleTimeSelectors);
|
||||
export const waffleOptionsSelectors = globalizeSelectors(selectLocal, localWaffleOptionsSelectors);
|
||||
|
|
|
@ -9,14 +9,7 @@ import { createEpicMiddleware } from 'redux-observable';
|
|||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
createRootEpic,
|
||||
initialState,
|
||||
logPositionSelectors,
|
||||
reducer,
|
||||
State,
|
||||
waffleTimeSelectors,
|
||||
} from '.';
|
||||
import { createRootEpic, initialState, reducer, State, waffleTimeSelectors } from '.';
|
||||
import { InfraApolloClient, InfraObservableApi } from '../lib/lib';
|
||||
|
||||
declare global {
|
||||
|
@ -36,10 +29,6 @@ export function createStore({ apolloClient, observableApi }: StoreDependencies)
|
|||
const middlewareDependencies = {
|
||||
postToApi$: observableApi.pipe(map(({ post }) => post)),
|
||||
apolloClient$: apolloClient,
|
||||
selectIsAutoReloadingLogEntries: logPositionSelectors.selectIsAutoReloading,
|
||||
selectIsAutoReloadingScrollLocked: logPositionSelectors.selectAutoReloadScrollLock,
|
||||
selectLogTargetPosition: logPositionSelectors.selectTargetPosition,
|
||||
selectVisibleLogMidpointOrTarget: logPositionSelectors.selectVisibleMidpointOrTarget,
|
||||
selectWaffleTimeUpdatePolicyInterval: waffleTimeSelectors.selectTimeUpdatePolicyInterval,
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
state: undefined,
|
||||
};
|
||||
const expectedSearchString =
|
||||
"logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194))&sourceId=default";
|
||||
"logFilter=(expression:'trace.id:433b4651687e18be2c6c8e3b11f53d09',kind:kuery)&logPosition=(position:(tiebreaker:0,time:1565707203194),streamLive:!f)&sourceId=default";
|
||||
const expectedRedirectPath = '/logs/stream?';
|
||||
|
||||
await pageObjects.common.navigateToActualUrl(
|
||||
|
|
Loading…
Reference in a new issue