[issues-211] - Clearing up messages from a topic (#378)
Co-authored-by: mbovtryuk <mbovtryuk@provectus.com>
This commit is contained in:
parent
ca4b3f12f9
commit
c86c955ace
15 changed files with 252 additions and 46 deletions
|
@ -21,6 +21,11 @@ interface Props {
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
fetchTopicsList(props: FetchTopicsListParams): void;
|
fetchTopicsList(props: FetchTopicsListParams): void;
|
||||||
deleteTopic(topicName: TopicName, clusterName: ClusterName): void;
|
deleteTopic(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
|
clearTopicMessages(
|
||||||
|
topicName: TopicName,
|
||||||
|
clusterName: ClusterName,
|
||||||
|
partitions?: number[]
|
||||||
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const List: React.FC<Props> = ({
|
const List: React.FC<Props> = ({
|
||||||
|
@ -30,6 +35,7 @@ const List: React.FC<Props> = ({
|
||||||
totalPages,
|
totalPages,
|
||||||
fetchTopicsList,
|
fetchTopicsList,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
|
clearTopicMessages,
|
||||||
}) => {
|
}) => {
|
||||||
const { isReadOnly } = React.useContext(ClusterContext);
|
const { isReadOnly } = React.useContext(ClusterContext);
|
||||||
const { clusterName } = useParams<{ clusterName: ClusterName }>();
|
const { clusterName } = useParams<{ clusterName: ClusterName }>();
|
||||||
|
@ -99,6 +105,7 @@ const List: React.FC<Props> = ({
|
||||||
key={topic.name}
|
key={topic.name}
|
||||||
topic={topic}
|
topic={topic}
|
||||||
deleteTopic={deleteTopic}
|
deleteTopic={deleteTopic}
|
||||||
|
clearTopicMessages={clearTopicMessages}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{items.length === 0 && (
|
{items.length === 0 && (
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RootState } from 'redux/interfaces';
|
import { RootState } from 'redux/interfaces';
|
||||||
import { fetchTopicsList, deleteTopic } from 'redux/actions';
|
import {
|
||||||
|
fetchTopicsList,
|
||||||
|
deleteTopic,
|
||||||
|
clearTopicMessages,
|
||||||
|
} from 'redux/actions';
|
||||||
import {
|
import {
|
||||||
getTopicList,
|
getTopicList,
|
||||||
getExternalTopicList,
|
getExternalTopicList,
|
||||||
|
@ -19,6 +23,7 @@ const mapStateToProps = (state: RootState) => ({
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
fetchTopicsList,
|
fetchTopicsList,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
|
clearTopicMessages,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(List);
|
export default connect(mapStateToProps, mapDispatchToProps)(List);
|
||||||
|
|
|
@ -14,12 +14,14 @@ export interface ListItemProps {
|
||||||
topic: TopicWithDetailedInfo;
|
topic: TopicWithDetailedInfo;
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
|
clearTopicMessages(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListItem: React.FC<ListItemProps> = ({
|
const ListItem: React.FC<ListItemProps> = ({
|
||||||
topic: { name, internal, partitions },
|
topic: { name, internal, partitions },
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
clusterName,
|
clusterName,
|
||||||
|
clearTopicMessages,
|
||||||
}) => {
|
}) => {
|
||||||
const [
|
const [
|
||||||
isDeleteTopicConfirmationVisible,
|
isDeleteTopicConfirmationVisible,
|
||||||
|
@ -41,6 +43,10 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
deleteTopic(clusterName, name);
|
deleteTopic(clusterName, name);
|
||||||
}, [clusterName, name]);
|
}, [clusterName, name]);
|
||||||
|
|
||||||
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
|
clearTopicMessages(clusterName, name);
|
||||||
|
}, [clusterName, name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
<td className="has-text-overflow-ellipsis">
|
<td className="has-text-overflow-ellipsis">
|
||||||
|
@ -70,6 +76,9 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
}
|
}
|
||||||
right
|
right
|
||||||
>
|
>
|
||||||
|
<DropdownItem onClick={clearTopicMessagesHandler}>
|
||||||
|
<span className="has-text-danger">Clear Messages</span>
|
||||||
|
</DropdownItem>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={() => setDeleteTopicConfirmationVisible(true)}
|
onClick={() => setDeleteTopicConfirmationVisible(true)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -23,6 +23,7 @@ describe('List', () => {
|
||||||
totalPages={1}
|
totalPages={1}
|
||||||
fetchTopicsList={jest.fn()}
|
fetchTopicsList={jest.fn()}
|
||||||
deleteTopic={jest.fn()}
|
deleteTopic={jest.fn()}
|
||||||
|
clearTopicMessages={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</ClusterContext.Provider>
|
</ClusterContext.Provider>
|
||||||
</StaticRouter>
|
</StaticRouter>
|
||||||
|
@ -49,6 +50,7 @@ describe('List', () => {
|
||||||
totalPages={1}
|
totalPages={1}
|
||||||
fetchTopicsList={jest.fn()}
|
fetchTopicsList={jest.fn()}
|
||||||
deleteTopic={jest.fn()}
|
deleteTopic={jest.fn()}
|
||||||
|
clearTopicMessages={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</ClusterContext.Provider>
|
</ClusterContext.Provider>
|
||||||
</StaticRouter>
|
</StaticRouter>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ListItem, { ListItemProps } from '../ListItem';
|
||||||
|
|
||||||
const mockDelete = jest.fn();
|
const mockDelete = jest.fn();
|
||||||
const clusterName = 'local';
|
const clusterName = 'local';
|
||||||
|
const mockDeleteMessages = jest.fn();
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
'components/common/ConfirmationModal/ConfirmationModal',
|
'components/common/ConfirmationModal/ConfirmationModal',
|
||||||
|
@ -21,14 +22,25 @@ describe('ListItem', () => {
|
||||||
topic={internalTopicPayload}
|
topic={internalTopicPayload}
|
||||||
deleteTopic={mockDelete}
|
deleteTopic={mockDelete}
|
||||||
clusterName={clusterName}
|
clusterName={clusterName}
|
||||||
|
clearTopicMessages={mockDeleteMessages}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('triggers the deleting messages when clicked on the delete messages button', () => {
|
||||||
|
const component = shallow(setupComponent());
|
||||||
|
component.find('DropdownItem').at(0).simulate('click');
|
||||||
|
expect(mockDeleteMessages).toBeCalledTimes(1);
|
||||||
|
expect(mockDeleteMessages).toBeCalledWith(
|
||||||
|
clusterName,
|
||||||
|
internalTopicPayload.name
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('triggers the deleteTopic when clicked on the delete button', () => {
|
it('triggers the deleteTopic when clicked on the delete button', () => {
|
||||||
const wrapper = shallow(setupComponent());
|
const wrapper = shallow(setupComponent());
|
||||||
expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy();
|
expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy();
|
||||||
wrapper.find('DropdownItem').last().simulate('click');
|
wrapper.find('DropdownItem').at(1).simulate('click');
|
||||||
const modal = wrapper.find('mock-ConfirmationModal');
|
const modal = wrapper.find('mock-ConfirmationModal');
|
||||||
expect(modal.prop('isOpen')).toBeTruthy();
|
expect(modal.prop('isOpen')).toBeTruthy();
|
||||||
modal.simulate('confirm');
|
modal.simulate('confirm');
|
||||||
|
|
|
@ -19,9 +19,15 @@ interface Props extends Topic, TopicDetails {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topicName: TopicName;
|
topicName: TopicName;
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
|
clearTopicMessages(clusterName: ClusterName, topicName: TopicName): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Details: React.FC<Props> = ({ clusterName, topicName, deleteTopic }) => {
|
const Details: React.FC<Props> = ({
|
||||||
|
clusterName,
|
||||||
|
topicName,
|
||||||
|
deleteTopic,
|
||||||
|
clearTopicMessages,
|
||||||
|
}) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { isReadOnly } = React.useContext(ClusterContext);
|
const { isReadOnly } = React.useContext(ClusterContext);
|
||||||
const [
|
const [
|
||||||
|
@ -33,6 +39,10 @@ const Details: React.FC<Props> = ({ clusterName, topicName, deleteTopic }) => {
|
||||||
history.push(clusterTopicsPath(clusterName));
|
history.push(clusterTopicsPath(clusterName));
|
||||||
}, [clusterName, topicName]);
|
}, [clusterName, topicName]);
|
||||||
|
|
||||||
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
|
clearTopicMessages(clusterName, topicName);
|
||||||
|
}, [clusterName, topicName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<nav className="navbar" role="navigation">
|
<nav className="navbar" role="navigation">
|
||||||
|
@ -66,6 +76,13 @@ const Details: React.FC<Props> = ({ clusterName, topicName, deleteTopic }) => {
|
||||||
<div className="buttons">
|
<div className="buttons">
|
||||||
{!isReadOnly && (
|
{!isReadOnly && (
|
||||||
<>
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="button is-danger"
|
||||||
|
onClick={clearTopicMessagesHandler}
|
||||||
|
>
|
||||||
|
Clear All Messages
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="button is-danger"
|
className="button is-danger"
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { ClusterName, RootState, TopicName } from 'redux/interfaces';
|
import { ClusterName, RootState, TopicName } from 'redux/interfaces';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
import { deleteTopic } from 'redux/actions';
|
import { deleteTopic, clearTopicMessages } from 'redux/actions';
|
||||||
|
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
|
@ -25,6 +26,7 @@ const mapStateToProps = (
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
|
clearTopicMessages,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(
|
export default withRouter(
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Topic, TopicDetails } from 'generated-sources';
|
import { Topic, TopicDetails } from 'generated-sources';
|
||||||
|
import { ClusterName, TopicName } from 'redux/interfaces';
|
||||||
|
import Dropdown from 'components/common/Dropdown/Dropdown';
|
||||||
|
import DropdownItem from 'components/common/Dropdown/DropdownItem';
|
||||||
import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
|
import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
|
||||||
import Indicator from 'components/common/Dashboard/Indicator';
|
import Indicator from 'components/common/Dashboard/Indicator';
|
||||||
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
|
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
|
||||||
|
|
||||||
interface Props extends Topic, TopicDetails {}
|
interface Props extends Topic, TopicDetails {
|
||||||
|
clusterName: ClusterName;
|
||||||
|
topicName: TopicName;
|
||||||
|
clearTopicMessages(
|
||||||
|
clusterName: ClusterName,
|
||||||
|
topicName: TopicName,
|
||||||
|
partitions?: number[]
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
const Overview: React.FC<Props> = ({
|
const Overview: React.FC<Props> = ({
|
||||||
partitions,
|
partitions,
|
||||||
|
@ -16,6 +27,9 @@ const Overview: React.FC<Props> = ({
|
||||||
replicationFactor,
|
replicationFactor,
|
||||||
segmentSize,
|
segmentSize,
|
||||||
segmentCount,
|
segmentCount,
|
||||||
|
clusterName,
|
||||||
|
topicName,
|
||||||
|
clearTopicMessages,
|
||||||
}) => (
|
}) => (
|
||||||
<>
|
<>
|
||||||
<MetricsWrapper>
|
<MetricsWrapper>
|
||||||
|
@ -43,7 +57,6 @@ const Overview: React.FC<Props> = ({
|
||||||
<Indicator label="Segment count">{segmentCount}</Indicator>
|
<Indicator label="Segment count">{segmentCount}</Indicator>
|
||||||
</MetricsWrapper>
|
</MetricsWrapper>
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="table-container">
|
|
||||||
<table className="table is-striped is-fullwidth">
|
<table className="table is-striped is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -51,6 +64,7 @@ const Overview: React.FC<Props> = ({
|
||||||
<th>Broker leader</th>
|
<th>Broker leader</th>
|
||||||
<th>Min offset</th>
|
<th>Min offset</th>
|
||||||
<th>Max offset</th>
|
<th>Max offset</th>
|
||||||
|
<th> </th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -60,12 +74,29 @@ const Overview: React.FC<Props> = ({
|
||||||
<td>{leader}</td>
|
<td>{leader}</td>
|
||||||
<td>{offsetMin}</td>
|
<td>{offsetMin}</td>
|
||||||
<td>{offsetMax}</td>
|
<td>{offsetMax}</td>
|
||||||
|
<td className="has-text-right">
|
||||||
|
<Dropdown
|
||||||
|
label={
|
||||||
|
<span className="icon">
|
||||||
|
<i className="fas fa-cog" />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
right
|
||||||
|
>
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() =>
|
||||||
|
clearTopicMessages(clusterName, topicName, [partition])
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="has-text-danger">Clear Messages</span>
|
||||||
|
</DropdownItem>
|
||||||
|
</Dropdown>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import { RootState, TopicName, ClusterName } from 'redux/interfaces';
|
import { RootState, TopicName, ClusterName } from 'redux/interfaces';
|
||||||
import { getTopicByName } from 'redux/reducers/topics/selectors';
|
import { getTopicByName } from 'redux/reducers/topics/selectors';
|
||||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||||
|
import { clearTopicMessages } from 'redux/actions';
|
||||||
import Overview from './Overview';
|
import Overview from './Overview';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
|
@ -15,11 +16,19 @@ const mapStateToProps = (
|
||||||
state: RootState,
|
state: RootState,
|
||||||
{
|
{
|
||||||
match: {
|
match: {
|
||||||
params: { topicName },
|
params: { topicName, clusterName },
|
||||||
},
|
},
|
||||||
}: OwnProps
|
}: OwnProps
|
||||||
) => ({
|
) => ({
|
||||||
...getTopicByName(state, topicName),
|
...getTopicByName(state, topicName),
|
||||||
|
topicName,
|
||||||
|
clusterName,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(connect(mapStateToProps)(Overview));
|
const mapDispatchToProps = {
|
||||||
|
clearTopicMessages,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRouter(
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)(Overview)
|
||||||
|
);
|
||||||
|
|
|
@ -109,4 +109,26 @@ describe('Actions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('clearMessagesTopicAction', () => {
|
||||||
|
it('creates a REQUEST action', () => {
|
||||||
|
expect(actions.clearMessagesTopicAction.request()).toEqual({
|
||||||
|
type: 'CLEAR_TOPIC_MESSAGES__REQUEST',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a SUCCESS action', () => {
|
||||||
|
expect(actions.clearMessagesTopicAction.success('topic')).toEqual({
|
||||||
|
type: 'CLEAR_TOPIC_MESSAGES__SUCCESS',
|
||||||
|
payload: 'topic',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a FAILURE action', () => {
|
||||||
|
expect(actions.clearMessagesTopicAction.failure({})).toEqual({
|
||||||
|
type: 'CLEAR_TOPIC_MESSAGES__FAILURE',
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,4 +43,54 @@ describe('Thunks', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('clearTopicMessages', () => {
|
||||||
|
it('creates CLEAR_TOPIC_MESSAGES__SUCCESS when deleting existing messages', async () => {
|
||||||
|
fetchMock.deleteOnce(
|
||||||
|
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||||
|
200
|
||||||
|
);
|
||||||
|
await store.dispatch(thunks.clearTopicMessages(clusterName, topicName));
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.clearMessagesTopicAction.request(),
|
||||||
|
actions.clearMessagesTopicAction.success(topicName),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates CLEAR_TOPIC_MESSAGES__FAILURE when deleting existing messages', async () => {
|
||||||
|
fetchMock.deleteOnce(
|
||||||
|
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||||
|
404
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await store.dispatch(thunks.clearTopicMessages(clusterName, topicName));
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.status).toEqual(404);
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.clearMessagesTopicAction.request(),
|
||||||
|
actions.clearMessagesTopicAction.failure({}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchTopicMessages', () => {
|
||||||
|
it('creates GET_TOPIC_MESSAGES__FAILURE when deleting existing messages', async () => {
|
||||||
|
fetchMock.getOnce(
|
||||||
|
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||||
|
404
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await store.dispatch(
|
||||||
|
thunks.fetchTopicMessages(clusterName, topicName, {})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.status).toEqual(404);
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.fetchTopicMessagesAction.request(),
|
||||||
|
actions.fetchTopicMessagesAction.failure(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,6 +60,12 @@ export const fetchTopicMessagesAction = createAsyncAction(
|
||||||
'GET_TOPIC_MESSAGES__FAILURE'
|
'GET_TOPIC_MESSAGES__FAILURE'
|
||||||
)<undefined, TopicMessage[], undefined>();
|
)<undefined, TopicMessage[], undefined>();
|
||||||
|
|
||||||
|
export const clearMessagesTopicAction = createAsyncAction(
|
||||||
|
'CLEAR_TOPIC_MESSAGES__REQUEST',
|
||||||
|
'CLEAR_TOPIC_MESSAGES__SUCCESS',
|
||||||
|
'CLEAR_TOPIC_MESSAGES__FAILURE'
|
||||||
|
)<undefined, TopicName, { alert?: FailurePayload }>();
|
||||||
|
|
||||||
export const fetchTopicDetailsAction = createAsyncAction(
|
export const fetchTopicDetailsAction = createAsyncAction(
|
||||||
'GET_TOPIC_DETAILS__REQUEST',
|
'GET_TOPIC_DETAILS__REQUEST',
|
||||||
'GET_TOPIC_DETAILS__SUCCESS',
|
'GET_TOPIC_DETAILS__SUCCESS',
|
||||||
|
|
|
@ -81,6 +81,30 @@ export const fetchTopicMessages = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clearTopicMessages = (
|
||||||
|
clusterName: ClusterName,
|
||||||
|
topicName: TopicName,
|
||||||
|
partitions?: number[]
|
||||||
|
): PromiseThunkResult => async (dispatch) => {
|
||||||
|
dispatch(actions.clearMessagesTopicAction.request());
|
||||||
|
try {
|
||||||
|
await messagesApiClient.deleteTopicMessages({
|
||||||
|
clusterName,
|
||||||
|
topicName,
|
||||||
|
partitions,
|
||||||
|
});
|
||||||
|
dispatch(actions.clearMessagesTopicAction.success(topicName));
|
||||||
|
} catch (e) {
|
||||||
|
const response = await getResponse(e);
|
||||||
|
const alert: FailurePayload = {
|
||||||
|
subject: [clusterName, topicName, partitions].join('-'),
|
||||||
|
title: `Clear Topic Messages`,
|
||||||
|
response,
|
||||||
|
};
|
||||||
|
dispatch(actions.clearMessagesTopicAction.failure({ alert }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchTopicDetails = (
|
export const fetchTopicDetails = (
|
||||||
clusterName: ClusterName,
|
clusterName: ClusterName,
|
||||||
topicName: TopicName
|
topicName: TopicName
|
||||||
|
|
|
@ -1,29 +1,33 @@
|
||||||
import { deleteTopicAction } from 'redux/actions';
|
import { deleteTopicAction, clearMessagesTopicAction } from 'redux/actions';
|
||||||
import reducer from '../reducer';
|
import reducer from '../reducer';
|
||||||
|
|
||||||
describe('topics reducer', () => {
|
|
||||||
it('deletes the topic from the list on DELETE_TOPIC__SUCCESS', () => {
|
|
||||||
const topic = {
|
const topic = {
|
||||||
name: 'topic',
|
name: 'topic',
|
||||||
id: 'id',
|
id: 'id',
|
||||||
};
|
};
|
||||||
expect(
|
|
||||||
reducer(
|
const state = {
|
||||||
{
|
|
||||||
byName: {
|
byName: {
|
||||||
[topic.name]: topic,
|
[topic.name]: topic,
|
||||||
},
|
},
|
||||||
allNames: [topic.name],
|
allNames: [topic.name],
|
||||||
messages: [],
|
messages: [],
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
},
|
};
|
||||||
deleteTopicAction.success(topic.name)
|
|
||||||
)
|
describe('topics reducer', () => {
|
||||||
).toEqual({
|
it('deletes the topic from the list on DELETE_TOPIC__SUCCESS', () => {
|
||||||
|
expect(reducer(state, deleteTopicAction.success(topic.name))).toEqual({
|
||||||
byName: {},
|
byName: {},
|
||||||
allNames: [],
|
allNames: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('delete topic messages on CLEAR_TOPIC_MESSAGES__SUCCESS', () => {
|
||||||
|
expect(
|
||||||
|
reducer(state, clearMessagesTopicAction.success(topic.name))
|
||||||
|
).toEqual(state);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,6 +53,12 @@ const reducer = (state = initialState, action: Action): TopicsState => {
|
||||||
);
|
);
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
case getType(actions.clearMessagesTopicAction.success): {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue