[Issue-998] Add Recreate topic button in to list of Topic and Details topic Overview (#1660)
* Add Recreate topic button in to list of Topic and Details topic Overview * Add reducer and update test * update reducer test * update Topic/Details test * Table and TableColumn components, TableState and DataSource * Table: Migrate topics table to new Table component * fix module paths * test for propertyLookup * improve useTableState code * fix folder name * improve table ordering Co-authored-by: Anton Zorin <zorii4@Antons-MacBook-Pro.local> Co-authored-by: Sash Stepanyan <sstepanyan@provectus.com> Co-authored-by: Sasha Stepanyan <100123785+sasunprov@users.noreply.github.com>
This commit is contained in:
parent
ad628ed7ae
commit
634406ac91
13 changed files with 194 additions and 2 deletions
|
@ -47,6 +47,7 @@ export interface TopicsListProps {
|
||||||
fetchTopicsList(props: GetTopicsRequest): void;
|
fetchTopicsList(props: GetTopicsRequest): void;
|
||||||
deleteTopic(topicName: TopicName, clusterName: ClusterName): void;
|
deleteTopic(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
deleteTopics(topicName: TopicName, clusterNames: ClusterName[]): void;
|
deleteTopics(topicName: TopicName, clusterNames: ClusterName[]): void;
|
||||||
|
recreateTopic(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
clearTopicsMessages(topicName: TopicName, clusterNames: ClusterName[]): void;
|
clearTopicsMessages(topicName: TopicName, clusterNames: ClusterName[]): void;
|
||||||
clearTopicMessages(
|
clearTopicMessages(
|
||||||
topicName: TopicName,
|
topicName: TopicName,
|
||||||
|
@ -67,6 +68,7 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
fetchTopicsList,
|
fetchTopicsList,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
deleteTopics,
|
deleteTopics,
|
||||||
|
recreateTopic,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
clearTopicsMessages,
|
clearTopicsMessages,
|
||||||
search,
|
search,
|
||||||
|
@ -169,6 +171,11 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
setDeleteTopicConfirmationVisible,
|
setDeleteTopicConfirmationVisible,
|
||||||
] = React.useState(false);
|
] = React.useState(false);
|
||||||
|
|
||||||
|
const [
|
||||||
|
isRecreateTopicConfirmationVisible,
|
||||||
|
setRecreateTopicConfirmationVisible,
|
||||||
|
] = React.useState(false);
|
||||||
|
|
||||||
const deleteTopicHandler = React.useCallback(() => {
|
const deleteTopicHandler = React.useCallback(() => {
|
||||||
deleteTopic(clusterName, name);
|
deleteTopic(clusterName, name);
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
@ -176,6 +183,12 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
clearTopicMessages(clusterName, name);
|
clearTopicMessages(clusterName, name);
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
|
const recreateTopicHandler = React.useCallback(() => {
|
||||||
|
recreateTopic(clusterName, name);
|
||||||
|
setRecreateTopicConfirmationVisible(false);
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!internal && !isReadOnly && hovered ? (
|
{!internal && !isReadOnly && hovered ? (
|
||||||
|
@ -194,6 +207,12 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
Remove Topic
|
Remove Topic
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() => setRecreateTopicConfirmationVisible(true)}
|
||||||
|
danger
|
||||||
|
>
|
||||||
|
Recreate Topic
|
||||||
|
</DropdownItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -204,6 +223,13 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
>
|
>
|
||||||
Are you sure want to remove <b>{name}</b> topic?
|
Are you sure want to remove <b>{name}</b> topic?
|
||||||
</ConfirmationModal>
|
</ConfirmationModal>
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={isRecreateTopicConfirmationVisible}
|
||||||
|
onCancel={() => setRecreateTopicConfirmationVisible(false)}
|
||||||
|
onConfirm={recreateTopicHandler}
|
||||||
|
>
|
||||||
|
Are you sure to recreate <b>{name}</b> topic?
|
||||||
|
</ConfirmationModal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
fetchTopicsList,
|
fetchTopicsList,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
deleteTopics,
|
deleteTopics,
|
||||||
|
recreateTopic,
|
||||||
clearTopicsMessages,
|
clearTopicsMessages,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
setTopicsSearchAction,
|
setTopicsSearchAction,
|
||||||
|
@ -33,6 +34,7 @@ const mapDispatchToProps = {
|
||||||
fetchTopicsList,
|
fetchTopicsList,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
deleteTopics,
|
deleteTopics,
|
||||||
|
recreateTopic,
|
||||||
clearTopicsMessages,
|
clearTopicsMessages,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
setTopicsSearch: setTopicsSearchAction,
|
setTopicsSearch: setTopicsSearchAction,
|
||||||
|
|
|
@ -20,6 +20,7 @@ export interface ListItemProps {
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
toggleTopicSelected(topicName: TopicName): void;
|
toggleTopicSelected(topicName: TopicName): void;
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
|
recreateTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
clearTopicMessages(topicName: TopicName, clusterName: ClusterName): void;
|
clearTopicMessages(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +37,7 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
selected,
|
selected,
|
||||||
toggleTopicSelected,
|
toggleTopicSelected,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
|
recreateTopic,
|
||||||
clusterName,
|
clusterName,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -45,6 +47,11 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
const [isDeleteTopicConfirmationVisible, setDeleteTopicConfirmationVisible] =
|
const [isDeleteTopicConfirmationVisible, setDeleteTopicConfirmationVisible] =
|
||||||
React.useState(false);
|
React.useState(false);
|
||||||
|
|
||||||
|
const [
|
||||||
|
isRecreateTopicConfirmationVisible,
|
||||||
|
setRecreateTopicConfirmationVisible,
|
||||||
|
] = React.useState(false);
|
||||||
|
|
||||||
const { outOfSyncReplicas, numberOfMessages } = React.useMemo(() => {
|
const { outOfSyncReplicas, numberOfMessages } = React.useMemo(() => {
|
||||||
if (partitions === undefined || partitions.length === 0) {
|
if (partitions === undefined || partitions.length === 0) {
|
||||||
return {
|
return {
|
||||||
|
@ -72,6 +79,11 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
deleteTopic(clusterName, name);
|
deleteTopic(clusterName, name);
|
||||||
}, [clusterName, deleteTopic, name]);
|
}, [clusterName, deleteTopic, name]);
|
||||||
|
|
||||||
|
const recreateTopicHandler = React.useCallback(() => {
|
||||||
|
recreateTopic(clusterName, name);
|
||||||
|
setRecreateTopicConfirmationVisible(false);
|
||||||
|
}, [recreateTopic, clusterName, name]);
|
||||||
|
|
||||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
clearTopicMessages(clusterName, name);
|
clearTopicMessages(clusterName, name);
|
||||||
}, [clearTopicMessages, clusterName, name]);
|
}, [clearTopicMessages, clusterName, name]);
|
||||||
|
@ -125,6 +137,12 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
Remove Topic
|
Remove Topic
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() => setRecreateTopicConfirmationVisible(true)}
|
||||||
|
danger
|
||||||
|
>
|
||||||
|
Recreate Topic
|
||||||
|
</DropdownItem>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -135,6 +153,13 @@ const ListItem: React.FC<ListItemProps> = ({
|
||||||
>
|
>
|
||||||
Are you sure want to remove <b>{name}</b> topic?
|
Are you sure want to remove <b>{name}</b> topic?
|
||||||
</ConfirmationModal>
|
</ConfirmationModal>
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={isRecreateTopicConfirmationVisible}
|
||||||
|
onCancel={() => setRecreateTopicConfirmationVisible(false)}
|
||||||
|
onConfirm={recreateTopicHandler}
|
||||||
|
>
|
||||||
|
Are you sure to recreate <b>{name}</b> topic?
|
||||||
|
</ConfirmationModal>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,6 +32,7 @@ describe('List', () => {
|
||||||
deleteTopics={jest.fn()}
|
deleteTopics={jest.fn()}
|
||||||
clearTopicsMessages={jest.fn()}
|
clearTopicsMessages={jest.fn()}
|
||||||
clearTopicMessages={jest.fn()}
|
clearTopicMessages={jest.fn()}
|
||||||
|
recreateTopic={jest.fn()}
|
||||||
search=""
|
search=""
|
||||||
orderBy={null}
|
orderBy={null}
|
||||||
sortOrder={SortOrder.ASC}
|
sortOrder={SortOrder.ASC}
|
||||||
|
|
|
@ -13,7 +13,7 @@ const mockDelete = jest.fn();
|
||||||
const clusterName = 'local';
|
const clusterName = 'local';
|
||||||
const mockDeleteMessages = jest.fn();
|
const mockDeleteMessages = jest.fn();
|
||||||
const mockToggleTopicSelected = jest.fn();
|
const mockToggleTopicSelected = jest.fn();
|
||||||
|
const mockRecreateTopic = jest.fn();
|
||||||
jest.mock(
|
jest.mock(
|
||||||
'components/common/ConfirmationModal/ConfirmationModal',
|
'components/common/ConfirmationModal/ConfirmationModal',
|
||||||
() => 'mock-ConfirmationModal'
|
() => 'mock-ConfirmationModal'
|
||||||
|
@ -35,6 +35,7 @@ describe('ListItem', () => {
|
||||||
deleteTopic={mockDelete}
|
deleteTopic={mockDelete}
|
||||||
clusterName={clusterName}
|
clusterName={clusterName}
|
||||||
clearTopicMessages={mockDeleteMessages}
|
clearTopicMessages={mockDeleteMessages}
|
||||||
|
recreateTopic={mockRecreateTopic}
|
||||||
selected={false}
|
selected={false}
|
||||||
toggleTopicSelected={mockToggleTopicSelected}
|
toggleTopicSelected={mockToggleTopicSelected}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -36,6 +36,7 @@ interface Props extends Topic, TopicDetails {
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
isDeletePolicy: boolean;
|
isDeletePolicy: boolean;
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
|
recreateTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
clearTopicMessages(clusterName: ClusterName, topicName: TopicName): void;
|
clearTopicMessages(clusterName: ClusterName, topicName: TopicName): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ const Details: React.FC<Props> = ({
|
||||||
isDeleted,
|
isDeleted,
|
||||||
isDeletePolicy,
|
isDeletePolicy,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
|
recreateTopic,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
}) => {
|
}) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -63,6 +65,10 @@ const Details: React.FC<Props> = ({
|
||||||
React.useState(false);
|
React.useState(false);
|
||||||
const [isClearTopicConfirmationVisible, setClearTopicConfirmationVisible] =
|
const [isClearTopicConfirmationVisible, setClearTopicConfirmationVisible] =
|
||||||
React.useState(false);
|
React.useState(false);
|
||||||
|
const [
|
||||||
|
isRecreateTopicConfirmationVisible,
|
||||||
|
setRecreateTopicConfirmationVisible,
|
||||||
|
] = React.useState(false);
|
||||||
const deleteTopicHandler = React.useCallback(() => {
|
const deleteTopicHandler = React.useCallback(() => {
|
||||||
deleteTopic(clusterName, topicName);
|
deleteTopic(clusterName, topicName);
|
||||||
}, [clusterName, topicName, deleteTopic]);
|
}, [clusterName, topicName, deleteTopic]);
|
||||||
|
@ -79,6 +85,11 @@ const Details: React.FC<Props> = ({
|
||||||
setClearTopicConfirmationVisible(false);
|
setClearTopicConfirmationVisible(false);
|
||||||
}, [clusterName, topicName, clearTopicMessages]);
|
}, [clusterName, topicName, clearTopicMessages]);
|
||||||
|
|
||||||
|
const recreateTopicHandler = React.useCallback(() => {
|
||||||
|
recreateTopic(clusterName, topicName);
|
||||||
|
setRecreateTopicConfirmationVisible(false);
|
||||||
|
}, [recreateTopic, clusterName, topicName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeading text={topicName}>
|
<PageHeading text={topicName}>
|
||||||
|
@ -116,6 +127,12 @@ const Details: React.FC<Props> = ({
|
||||||
Clear messages
|
Clear messages
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
)}
|
)}
|
||||||
|
<DropdownItem
|
||||||
|
onClick={() => setRecreateTopicConfirmationVisible(true)}
|
||||||
|
danger
|
||||||
|
>
|
||||||
|
Recreate Topic
|
||||||
|
</DropdownItem>
|
||||||
{isTopicDeletionAllowed && (
|
{isTopicDeletionAllowed && (
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={() => setDeleteTopicConfirmationVisible(true)}
|
onClick={() => setDeleteTopicConfirmationVisible(true)}
|
||||||
|
@ -143,6 +160,13 @@ const Details: React.FC<Props> = ({
|
||||||
>
|
>
|
||||||
Are you sure want to clear topic messages?
|
Are you sure want to clear topic messages?
|
||||||
</ConfirmationModal>
|
</ConfirmationModal>
|
||||||
|
<ConfirmationModal
|
||||||
|
isOpen={isRecreateTopicConfirmationVisible}
|
||||||
|
onCancel={() => setRecreateTopicConfirmationVisible(false)}
|
||||||
|
onConfirm={recreateTopicHandler}
|
||||||
|
>
|
||||||
|
Are you sure want to recreate <b>{topicName}</b> topic?
|
||||||
|
</ConfirmationModal>
|
||||||
<Navbar role="navigation">
|
<Navbar role="navigation">
|
||||||
<NavLink
|
<NavLink
|
||||||
exact
|
exact
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
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, clearTopicMessages } from 'redux/actions';
|
import { deleteTopic, clearTopicMessages, recreateTopic } from 'redux/actions';
|
||||||
import {
|
import {
|
||||||
getIsTopicDeleted,
|
getIsTopicDeleted,
|
||||||
getIsTopicDeletePolicy,
|
getIsTopicDeletePolicy,
|
||||||
|
@ -33,6 +33,7 @@ const mapStateToProps = (
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
|
recreateTopic,
|
||||||
deleteTopic,
|
deleteTopic,
|
||||||
clearTopicMessages,
|
clearTopicMessages,
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ describe('Details', () => {
|
||||||
const mockClusterName = 'local';
|
const mockClusterName = 'local';
|
||||||
const mockClearTopicMessages = jest.fn();
|
const mockClearTopicMessages = jest.fn();
|
||||||
const mockInternalTopicPayload = internalTopicPayload.internal;
|
const mockInternalTopicPayload = internalTopicPayload.internal;
|
||||||
|
const mockRecreateTopic = jest.fn();
|
||||||
|
|
||||||
const setupComponent = (pathname: string) =>
|
const setupComponent = (pathname: string) =>
|
||||||
render(
|
render(
|
||||||
|
@ -29,6 +30,7 @@ describe('Details', () => {
|
||||||
name={internalTopicPayload.name}
|
name={internalTopicPayload.name}
|
||||||
isInternal={false}
|
isInternal={false}
|
||||||
deleteTopic={mockDelete}
|
deleteTopic={mockDelete}
|
||||||
|
recreateTopic={mockRecreateTopic}
|
||||||
clearTopicMessages={mockClearTopicMessages}
|
clearTopicMessages={mockClearTopicMessages}
|
||||||
isDeleted={false}
|
isDeleted={false}
|
||||||
isDeletePolicy
|
isDeletePolicy
|
||||||
|
@ -54,6 +56,7 @@ describe('Details', () => {
|
||||||
name={internalTopicPayload.name}
|
name={internalTopicPayload.name}
|
||||||
isInternal={mockInternalTopicPayload}
|
isInternal={mockInternalTopicPayload}
|
||||||
deleteTopic={mockDelete}
|
deleteTopic={mockDelete}
|
||||||
|
recreateTopic={mockRecreateTopic}
|
||||||
clearTopicMessages={mockClearTopicMessages}
|
clearTopicMessages={mockClearTopicMessages}
|
||||||
isDeleted={false}
|
isDeleted={false}
|
||||||
isDeletePolicy
|
isDeletePolicy
|
||||||
|
@ -77,4 +80,40 @@ describe('Details', () => {
|
||||||
getByText(/Are you sure want to clear topic messages?/i)
|
getByText(/Are you sure want to clear topic messages?/i)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shows a confirmation popup on recreating topic', () => {
|
||||||
|
setupComponent(
|
||||||
|
clusterTopicPath(mockClusterName, internalTopicPayload.name)
|
||||||
|
);
|
||||||
|
const recreateTopicButton = screen.getByText(/Recreate topic/i);
|
||||||
|
userEvent.click(recreateTopicButton);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByText(/Are you sure want to recreate topic?/i)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calling recreation function after click on Submit button', () => {
|
||||||
|
setupComponent(
|
||||||
|
clusterTopicPath(mockClusterName, internalTopicPayload.name)
|
||||||
|
);
|
||||||
|
const recreateTopicButton = screen.getByText(/Recreate topic/i);
|
||||||
|
userEvent.click(recreateTopicButton);
|
||||||
|
const confirmBtn = screen.getByRole('button', { name: /submit/i });
|
||||||
|
userEvent.click(confirmBtn);
|
||||||
|
expect(mockRecreateTopic).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('close popup confirmation window after click on Cancel button', () => {
|
||||||
|
setupComponent(
|
||||||
|
clusterTopicPath(mockClusterName, internalTopicPayload.name)
|
||||||
|
);
|
||||||
|
const recreateTopicButton = screen.getByText(/Recreate topic/i);
|
||||||
|
userEvent.click(recreateTopicButton);
|
||||||
|
const cancelBtn = screen.getByRole('button', { name: /cancel/i });
|
||||||
|
userEvent.click(cancelBtn);
|
||||||
|
expect(
|
||||||
|
screen.queryByText(/Are you sure want to recreate topic?/i)
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { mockTopicsState } from 'redux/actions/__test__/fixtures';
|
||||||
import { MessageSchemaSourceEnum, TopicMessageSchema } from 'generated-sources';
|
import { MessageSchemaSourceEnum, TopicMessageSchema } from 'generated-sources';
|
||||||
import { FailurePayload } from 'redux/interfaces';
|
import { FailurePayload } from 'redux/interfaces';
|
||||||
import { getResponse } from 'lib/errorHandling';
|
import { getResponse } from 'lib/errorHandling';
|
||||||
|
import { internalTopicPayload } from 'redux/reducers/topics/__test__/fixtures';
|
||||||
|
|
||||||
const store = mockStoreCreator;
|
const store = mockStoreCreator;
|
||||||
|
|
||||||
|
@ -49,6 +50,37 @@ describe('Thunks', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('recreateTopic', () => {
|
||||||
|
it('creates RECREATE_TOPIC__SUCCESS when recreating existing topic', async () => {
|
||||||
|
fetchMock.postOnce(
|
||||||
|
`/api/clusters/${clusterName}/topics/${topicName}`,
|
||||||
|
internalTopicPayload
|
||||||
|
);
|
||||||
|
await store.dispatch(thunks.recreateTopic(clusterName, topicName));
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.recreateTopicAction.request(),
|
||||||
|
actions.recreateTopicAction.success(internalTopicPayload),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates RECREATE_TOPIC__FAILURE when recreating existing topic', async () => {
|
||||||
|
fetchMock.postOnce(
|
||||||
|
`/api/clusters/${clusterName}/topics/${topicName}`,
|
||||||
|
404
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await store.dispatch(thunks.recreateTopic(clusterName, topicName));
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Response;
|
||||||
|
expect(err.status).toEqual(404);
|
||||||
|
expect(store.getActions()).toEqual([
|
||||||
|
actions.recreateTopicAction.request(),
|
||||||
|
actions.recreateTopicAction.failure(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('clearTopicMessages', () => {
|
describe('clearTopicMessages', () => {
|
||||||
it('creates CLEAR_TOPIC_MESSAGES__SUCCESS when deleting existing messages', async () => {
|
it('creates CLEAR_TOPIC_MESSAGES__SUCCESS when deleting existing messages', async () => {
|
||||||
fetchMock.deleteOnce(
|
fetchMock.deleteOnce(
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
FullConnectorInfo,
|
FullConnectorInfo,
|
||||||
Connect,
|
Connect,
|
||||||
Task,
|
Task,
|
||||||
|
Topic,
|
||||||
TopicMessage,
|
TopicMessage,
|
||||||
TopicMessageConsuming,
|
TopicMessageConsuming,
|
||||||
TopicMessageSchema,
|
TopicMessageSchema,
|
||||||
|
@ -60,6 +61,13 @@ export const deleteTopicAction = createAsyncAction(
|
||||||
'DELETE_TOPIC__CANCEL'
|
'DELETE_TOPIC__CANCEL'
|
||||||
)<undefined, TopicName, undefined, undefined>();
|
)<undefined, TopicName, undefined, undefined>();
|
||||||
|
|
||||||
|
export const recreateTopicAction = createAsyncAction(
|
||||||
|
'RECREATE_TOPIC__REQUEST',
|
||||||
|
'RECREATE_TOPIC__SUCCESS',
|
||||||
|
'RECREATE_TOPIC__FAILURE',
|
||||||
|
'RECREATE_TOPIC__CANCEL'
|
||||||
|
)<undefined, Topic, undefined, undefined>();
|
||||||
|
|
||||||
export const dismissAlert = createAction('DISMISS_ALERT')<string>();
|
export const dismissAlert = createAction('DISMISS_ALERT')<string>();
|
||||||
|
|
||||||
export const fetchConnectsAction = createAsyncAction(
|
export const fetchConnectsAction = createAsyncAction(
|
||||||
|
|
|
@ -259,6 +259,21 @@ export const deleteTopic =
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const recreateTopic =
|
||||||
|
(clusterName: ClusterName, topicName: TopicName): PromiseThunkResult =>
|
||||||
|
async (dispatch) => {
|
||||||
|
dispatch(actions.recreateTopicAction.request());
|
||||||
|
try {
|
||||||
|
const topic = await topicsApiClient.recreateTopic({
|
||||||
|
clusterName,
|
||||||
|
topicName,
|
||||||
|
});
|
||||||
|
dispatch(actions.recreateTopicAction.success(topic));
|
||||||
|
} catch (e) {
|
||||||
|
dispatch(actions.recreateTopicAction.failure());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const deleteTopics =
|
export const deleteTopics =
|
||||||
(clusterName: ClusterName, topicsName: TopicName[]): PromiseThunkResult =>
|
(clusterName: ClusterName, topicsName: TopicName[]): PromiseThunkResult =>
|
||||||
async (dispatch) => {
|
async (dispatch) => {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
setTopicsOrderByAction,
|
setTopicsOrderByAction,
|
||||||
fetchTopicConsumerGroupsAction,
|
fetchTopicConsumerGroupsAction,
|
||||||
fetchTopicMessageSchemaAction,
|
fetchTopicMessageSchemaAction,
|
||||||
|
recreateTopicAction,
|
||||||
} from 'redux/actions';
|
} from 'redux/actions';
|
||||||
import reducer from 'redux/reducers/topics/reducer';
|
import reducer from 'redux/reducers/topics/reducer';
|
||||||
|
|
||||||
|
@ -94,6 +95,15 @@ describe('topics reducer', () => {
|
||||||
it('delete topic messages on CLEAR_TOPIC_MESSAGES__SUCCESS', () => {
|
it('delete topic messages on CLEAR_TOPIC_MESSAGES__SUCCESS', () => {
|
||||||
expect(reducer(state, clearMessagesTopicAction.success())).toEqual(state);
|
expect(reducer(state, clearMessagesTopicAction.success())).toEqual(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('recreate topic', () => {
|
||||||
|
expect(reducer(state, recreateTopicAction.success(topic))).toEqual({
|
||||||
|
...state,
|
||||||
|
byName: {
|
||||||
|
[topic.name]: topic,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('search topics', () => {
|
describe('search topics', () => {
|
||||||
|
|
|
@ -32,6 +32,14 @@ const reducer = (state = initialState, action: Action): TopicsState => {
|
||||||
);
|
);
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
case getType(actions.recreateTopicAction.success):
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
byName: {
|
||||||
|
...state.byName,
|
||||||
|
[action.payload.name]: { ...action.payload },
|
||||||
|
},
|
||||||
|
};
|
||||||
case getType(actions.setTopicsSearchAction): {
|
case getType(actions.setTopicsSearchAction): {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
Loading…
Add table
Reference in a new issue