Skip to content

Commit

Permalink
Issues/1740 live tailing improvements (#1774)
Browse files Browse the repository at this point in the history
* Implementing Context to the Topic messages pages

* Using TopicContext in the Topics Topic MessageTable component

* Using TopicContext variable in the Filters component

* Fixing the Ordering of the Live mode Topic messaging

* Fixing isLive parameter bug during page refresh

* Minor code modification in Topic Filter Message page

* Implement the correct seekType during live mode in url as well as in api call

* Add Test cases to Messages and refactor eventSource Mock

* Add initial Testing file for messages table

* improve the MessagesTable test File

* improve the MessagesTable test File + Filter Test File

* improve the MessagesTable test File

* Change the function name toggleSeekDirection  to changeSeekDirection

* change the name of the test suites to be more declarative

* Display the table progress bar in live mode only when no data is fetched
  • Loading branch information
Mgrdich authored Apr 5, 2022
1 parent c79905c commit 68f8eed
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from 'lib/testHelpers';
import { render, EventSourceMock } from 'lib/testHelpers';
import React from 'react';
import Query, {
getFormattedErrorFromTableData,
Expand All @@ -20,27 +20,6 @@ const renderComponent = () =>
}
);

// Small mock to get rid of reference error
class EventSourceMock {
url: string;

close: () => void;

open: () => void;

error: () => void;

onmessage: () => void;

constructor(url: string) {
this.url = url;
this.open = jest.fn();
this.error = jest.fn();
this.onmessage = jest.fn();
this.close = jest.fn();
}
}

describe('Query', () => {
it('renders', () => {
renderComponent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TopicMessageEventTypeEnum,
MessageFilterType,
} from 'generated-sources';
import React from 'react';
import React, { useContext } from 'react';
import { omitBy } from 'lodash';
import { useHistory, useLocation } from 'react-router';
import DatePicker from 'react-datepicker';
Expand All @@ -25,6 +25,8 @@ import { Button } from 'components/common/Button/Button';
import FilterModal, {
FilterEdit,
} from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';

import * as S from './Filters.styled';
import {
Expand Down Expand Up @@ -66,11 +68,6 @@ export const SeekTypeOptions = [
{ value: SeekType.OFFSET, label: 'Offset' },
{ value: SeekType.TIMESTAMP, label: 'Timestamp' },
];
export const SeekDirectionOptions = [
{ value: SeekDirection.FORWARD, label: 'Oldest First', isLive: false },
{ value: SeekDirection.BACKWARD, label: 'Newest First', isLive: false },
{ value: SeekDirection.TAILING, label: 'Live Mode', isLive: true },
];

const Filters: React.FC<FiltersProps> = ({
clusterName,
Expand All @@ -88,16 +85,14 @@ const Filters: React.FC<FiltersProps> = ({
const location = useLocation();
const history = useHistory();

const { searchParams, seekDirection, isLive, changeSeekDirection } =
useContext(TopicMessagesContext);

const [isOpen, setIsOpen] = React.useState(false);
const toggleIsOpen = () => setIsOpen(!isOpen);

const source = React.useRef<EventSource | null>(null);

const searchParams = React.useMemo(
() => new URLSearchParams(location.search),
[location]
);

const [selectedPartitions, setSelectedPartitions] = React.useState<Option[]>(
getSelectedPartitionsFromSeekToParam(searchParams, partitions)
);
Expand Down Expand Up @@ -132,10 +127,6 @@ const Filters: React.FC<FiltersProps> = ({
: MessageFilterType.STRING_CONTAINS
);
const [query, setQuery] = React.useState<string>(searchParams.get('q') || '');
const [seekDirection, setSeekDirection] = React.useState<SeekDirection>(
(searchParams.get('seekDirection') as SeekDirection) ||
SeekDirection.FORWARD
);
const isSeekTypeControlVisible = React.useMemo(
() => selectedPartitions.length > 0,
[selectedPartitions]
Expand Down Expand Up @@ -178,7 +169,7 @@ const Filters: React.FC<FiltersProps> = ({
setAttempt(attempt + 1);

if (isSeekTypeControlVisible) {
props.seekType = currentSeekType;
props.seekType = isLive ? SeekType.LATEST : currentSeekType;
props.seekTo = selectedPartitions.map(({ value }) => {
let seekToOffset;

Expand Down Expand Up @@ -217,21 +208,6 @@ const Filters: React.FC<FiltersProps> = ({
query,
]);

const toggleSeekDirection = (val: string) => {
switch (val) {
case SeekDirection.FORWARD:
setSeekDirection(SeekDirection.FORWARD);
break;
case SeekDirection.BACKWARD:
setSeekDirection(SeekDirection.BACKWARD);
break;
case SeekDirection.TAILING:
setSeekDirection(SeekDirection.TAILING);
break;
default:
}
};

const handleSSECancel = () => {
if (!source.current) return;

Expand Down Expand Up @@ -295,7 +271,7 @@ const Filters: React.FC<FiltersProps> = ({
};
// eslint-disable-next-line consistent-return
React.useEffect(() => {
if (location.search.length !== 0) {
if (location.search?.length !== 0) {
const url = `${BASE_PARAMS.basePath}/api/clusters/${clusterName}/topics/${topicName}/messages${location.search}`;
const sse = new EventSource(url);

Expand Down Expand Up @@ -346,7 +322,7 @@ const Filters: React.FC<FiltersProps> = ({
updatePhase,
]);
React.useEffect(() => {
if (location.search.length === 0) {
if (location.search?.length === 0) {
handleFiltersSubmit();
}
}, [handleFiltersSubmit, location]);
Expand Down Expand Up @@ -376,7 +352,7 @@ const Filters: React.FC<FiltersProps> = ({
selectSize="M"
minWidth="100px"
options={SeekTypeOptions}
disabled={seekDirection === SeekDirection.TAILING}
disabled={isLive}
/>
{currentSeekType === SeekType.OFFSET ? (
<Input
Expand All @@ -387,7 +363,7 @@ const Filters: React.FC<FiltersProps> = ({
className="offset-selector"
placeholder="Offset"
onChange={({ target: { value } }) => setOffset(value)}
disabled={seekDirection === SeekDirection.TAILING}
disabled={isLive}
/>
) : (
<DatePicker
Expand All @@ -398,7 +374,7 @@ const Filters: React.FC<FiltersProps> = ({
dateFormat="MMMM d, yyyy HH:mm"
className="date-picker"
placeholderText="Select timestamp"
disabled={seekDirection === SeekDirection.TAILING}
disabled={isLive}
/>
)}
</S.SeekTypeSelectorWrapper>
Expand Down Expand Up @@ -440,11 +416,11 @@ const Filters: React.FC<FiltersProps> = ({
</S.FilterInputs>
<Select
selectSize="M"
onChange={(option) => toggleSeekDirection(option as string)}
onChange={(option) => changeSeekDirection(option as string)}
value={seekDirection}
minWidth="120px"
options={SeekDirectionOptions}
isLive={seekDirection === SeekDirection.TAILING}
isLive={isLive}
/>
</div>
<S.ActiveSmartFilterWrapper>
Expand Down Expand Up @@ -479,12 +455,12 @@ const Filters: React.FC<FiltersProps> = ({
isFetching &&
phaseMessage}
</p>
<S.MessageLoading isLive={seekDirection === SeekDirection.TAILING}>
<S.MessageLoading isLive={isLive}>
<S.MessageLoadingSpinner isFetching={isFetching} />
Loading messages.
<S.StopLoading
onClick={() => {
setSeekDirection(SeekDirection.FORWARD);
changeSeekDirection(SeekDirection.FORWARD);
setIsFetching(false);
}}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,46 @@
import React from 'react';
import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
import Filters, {
FiltersProps,
SeekDirectionOptions,
SeekTypeOptions,
} from 'components/Topics/Topic/Details/Messages/Filters/Filters';
import { render } from 'lib/testHelpers';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TopicMessagesContext, {
ContextProps,
} from 'components/contexts/TopicMessagesContext';
import { SeekDirection } from 'generated-sources';

const setupWrapper = (props?: Partial<FiltersProps>) =>
const defaultContextValue: ContextProps = {
isLive: false,
seekDirection: SeekDirection.FORWARD,
searchParams: new URLSearchParams(''),
changeSeekDirection: jest.fn(),
};

const setupWrapper = (
props: Partial<FiltersProps> = {},
ctx: ContextProps = defaultContextValue
) => {
render(
<Filters
clusterName="test-cluster"
topicName="test-topic"
partitions={[{ partition: 0, offsetMin: 0, offsetMax: 100 }]}
meta={{}}
isFetching={false}
addMessage={jest.fn()}
resetMessages={jest.fn()}
updatePhase={jest.fn()}
updateMeta={jest.fn()}
setIsFetching={jest.fn()}
{...props}
/>
<TopicMessagesContext.Provider value={ctx}>
<Filters
clusterName="test-cluster"
topicName="test-topic"
partitions={[{ partition: 0, offsetMin: 0, offsetMax: 100 }]}
meta={{}}
isFetching={false}
addMessage={jest.fn()}
resetMessages={jest.fn()}
updatePhase={jest.fn()}
updateMeta={jest.fn()}
setIsFetching={jest.fn()}
{...props}
/>
</TopicMessagesContext.Provider>
);
};
describe('Filters component', () => {
it('renders component', () => {
setupWrapper();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,84 @@
import React from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
import { SeekDirection } from 'generated-sources';
import { useLocation } from 'react-router';

import FiltersContainer from './Filters/FiltersContainer';
import MessagesTable from './MessagesTable';

const Messages: React.FC = () => (
<div>
<FiltersContainer />
<MessagesTable />
</div>
);
export const SeekDirectionOptionsObj = {
[SeekDirection.FORWARD]: {
value: SeekDirection.FORWARD,
label: 'Oldest First',
isLive: false,
},
[SeekDirection.BACKWARD]: {
value: SeekDirection.BACKWARD,
label: 'Newest First',
isLive: false,
},
[SeekDirection.TAILING]: {
value: SeekDirection.TAILING,
label: 'Live Mode',
isLive: true,
},
};

export const SeekDirectionOptions = Object.values(SeekDirectionOptionsObj);

const Messages: React.FC = () => {
const location = useLocation();

const searchParams = React.useMemo(
() => new URLSearchParams(location.search),
[location.search]
);

const defaultSeekValue = SeekDirectionOptions[0];

const [seekDirection, setSeekDirection] = React.useState<SeekDirection>(
(searchParams.get('seekDirection') as SeekDirection) ||
defaultSeekValue.value
);

const [isLive, setIsLive] = useState<boolean>(
SeekDirectionOptionsObj[seekDirection].isLive
);

const changeSeekDirection = useCallback((val: string) => {
switch (val) {
case SeekDirection.FORWARD:
setSeekDirection(SeekDirection.FORWARD);
setIsLive(SeekDirectionOptionsObj[SeekDirection.FORWARD].isLive);
break;
case SeekDirection.BACKWARD:
setSeekDirection(SeekDirection.BACKWARD);
setIsLive(SeekDirectionOptionsObj[SeekDirection.BACKWARD].isLive);
break;
case SeekDirection.TAILING:
setSeekDirection(SeekDirection.TAILING);
setIsLive(SeekDirectionOptionsObj[SeekDirection.TAILING].isLive);
break;
default:
}
}, []);

const contextValue = useMemo(
() => ({
seekDirection,
searchParams,
changeSeekDirection,
isLive,
}),
[seekDirection, searchParams, changeSeekDirection]
);

return (
<TopicMessagesContext.Provider value={contextValue}>
<FiltersContainer />
<MessagesTable />
</TopicMessagesContext.Provider>
);
};

export default Messages;
Loading

0 comments on commit 68f8eed

Please sign in to comment.