Refactor TopicMessages's store to redux/toolkit (#1902)
* Refactor TopicMessages's store to redux/toolkit * adding test cases to improve coverage * fixing eslint error * removing extra code * Transform redux connect reducer and actions into toolkit slice. (#1883) * integrate redux toolkit for connects * uncomment test case * remove unnecessary comment * reduce duplication * Implement code highlighting for smart filters (#1868) * Implement code highlighting for smart filters * delete unnecessary code * fixing eslint problem * fixing sonar code smells (#1826) * fixing sonar code smells * removing unnecessary change * fixing some sonar code smell issues * making requested changes * Fix sonar badges in readme (#1906) * fixing merge conflicts Co-authored-by: Oleg Shur <workshur@gmail.com> * Add positive notifications after some successful actions. (#1830) * add some positive notifications after successful actions * some improvements * improve alerts reducer tests * Fix test warnings (#1908) * fix fetch mock warnings in brokers.spec.tsx * use separate waitFor-s for fetch-mock call expects * Persist show internal topics switch state (#1832) * persist show internal topics switch state * minor improvements in topics list tests * prevent duplication in topic list test file * fix seek type select border-radius and user-select (#1904) * Refetch topics list to display the updated data (#1900) * refetch topics list to display the updated data * fixing sonar code smells (#1826) * fixing sonar code smells * removing unnecessary change * fixing some sonar code smell issues * making requested changes * Fix sonar badges in readme (#1906) Co-authored-by: Robert Azizbekyan <103438454+rAzizbekyan@users.noreply.github.com> Co-authored-by: Oleg Shur <workshur@gmail.com> * Show confirmation modal when clear messages is clicked in topics list (#1899) * show confirmation modal when clear messages is clicked in topics list * change variable name * add missing dependencies * use useModal hook for topics list modals * display a human-readable error message for topic custom parameers value field (#1896) * modify dependencies to fix partitions filter bug (#1901) Co-authored-by: Arsen Simonyan <103444767+simonyandev@users.noreply.github.com> Co-authored-by: Oleg Shur <workshur@gmail.com>
This commit is contained in:
parent
fa6f587f97
commit
81d1e955da
20 changed files with 463 additions and 376 deletions
470
kafka-ui-react-app/package-lock.json
generated
470
kafka-ui-react-app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -50,11 +50,11 @@ export interface TopicsListProps {
|
||||||
deleteTopics(topicName: TopicName, clusterNames: ClusterName[]): void;
|
deleteTopics(topicName: TopicName, clusterNames: ClusterName[]): void;
|
||||||
recreateTopic(topicName: TopicName, clusterName: ClusterName): void;
|
recreateTopic(topicName: TopicName, clusterName: ClusterName): void;
|
||||||
clearTopicsMessages(topicName: TopicName, clusterNames: ClusterName[]): void;
|
clearTopicsMessages(topicName: TopicName, clusterNames: ClusterName[]): void;
|
||||||
clearTopicMessages(
|
clearTopicMessages(params: {
|
||||||
topicName: TopicName,
|
topicName: TopicName;
|
||||||
clusterName: ClusterName,
|
clusterName: ClusterName;
|
||||||
partitions?: number[]
|
partitions?: number[];
|
||||||
): void;
|
}): void;
|
||||||
search: string;
|
search: string;
|
||||||
orderBy: TopicColumnsToSort | null;
|
orderBy: TopicColumnsToSort | null;
|
||||||
sortOrder: SortOrder;
|
sortOrder: SortOrder;
|
||||||
|
@ -225,7 +225,7 @@ const List: React.FC<TopicsListProps> = ({
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
clearTopicMessages(clusterName, name);
|
clearTopicMessages({ clusterName, topicName: name });
|
||||||
fetchTopicsList(topicsListParams);
|
fetchTopicsList(topicsListParams);
|
||||||
closeClearMessagesModal();
|
closeClearMessagesModal();
|
||||||
}, [name, fetchTopicsList, topicsListParams]);
|
}, [name, fetchTopicsList, topicsListParams]);
|
||||||
|
|
|
@ -6,10 +6,10 @@ import {
|
||||||
deleteTopics,
|
deleteTopics,
|
||||||
recreateTopic,
|
recreateTopic,
|
||||||
clearTopicsMessages,
|
clearTopicsMessages,
|
||||||
clearTopicMessages,
|
|
||||||
setTopicsSearchAction,
|
setTopicsSearchAction,
|
||||||
setTopicsOrderByAction,
|
setTopicsOrderByAction,
|
||||||
} from 'redux/actions';
|
} from 'redux/actions';
|
||||||
|
import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import {
|
import {
|
||||||
getTopicList,
|
getTopicList,
|
||||||
getAreTopicsFetching,
|
getAreTopicsFetching,
|
||||||
|
|
|
@ -298,10 +298,12 @@ describe('List', () => {
|
||||||
|
|
||||||
it('triggers the deleteTopics when clicked on the delete button', async () => {
|
it('triggers the deleteTopics when clicked on the delete button', async () => {
|
||||||
await checkActionButtonClick('deleteTopics');
|
await checkActionButtonClick('deleteTopics');
|
||||||
|
expect(mockDeleteTopics).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('triggers the clearTopicsMessages when clicked on the clear button', async () => {
|
it('triggers the clearTopicsMessages when clicked on the clear button', async () => {
|
||||||
await checkActionButtonClick('clearTopicsMessages');
|
await checkActionButtonClick('clearTopicsMessages');
|
||||||
|
expect(mockClearTopicsMessages).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes ConfirmationModal when clicked on the cancel button', async () => {
|
it('closes ConfirmationModal when clicked on the cancel button', async () => {
|
||||||
|
|
|
@ -37,7 +37,10 @@ interface Props extends Topic, TopicDetails {
|
||||||
isDeletePolicy: boolean;
|
isDeletePolicy: boolean;
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
recreateTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
recreateTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
||||||
clearTopicMessages(clusterName: ClusterName, topicName: TopicName): void;
|
clearTopicMessages(params: {
|
||||||
|
clusterName: ClusterName;
|
||||||
|
topicName: TopicName;
|
||||||
|
}): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderControlsWrapper = styled.div`
|
const HeaderControlsWrapper = styled.div`
|
||||||
|
@ -81,7 +84,7 @@ const Details: React.FC<Props> = ({
|
||||||
}, [isDeleted, clusterName, dispatch, history]);
|
}, [isDeleted, clusterName, dispatch, history]);
|
||||||
|
|
||||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
const clearTopicMessagesHandler = React.useCallback(() => {
|
||||||
clearTopicMessages(clusterName, topicName);
|
clearTopicMessages({ clusterName, topicName });
|
||||||
setClearTopicConfirmationVisible(false);
|
setClearTopicConfirmationVisible(false);
|
||||||
}, [clusterName, topicName, clearTopicMessages]);
|
}, [clusterName, topicName, clearTopicMessages]);
|
||||||
|
|
||||||
|
|
|
@ -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, clearTopicMessages, recreateTopic } from 'redux/actions';
|
import { deleteTopic, recreateTopic } from 'redux/actions';
|
||||||
|
import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import {
|
import {
|
||||||
getIsTopicDeleted,
|
getIsTopicDeleted,
|
||||||
getIsTopicDeletePolicy,
|
getIsTopicDeletePolicy,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
updateTopicMessagesMeta,
|
updateTopicMessagesMeta,
|
||||||
updateTopicMessagesPhase,
|
updateTopicMessagesPhase,
|
||||||
setTopicMessagesFetchingStatus,
|
setTopicMessagesFetchingStatus,
|
||||||
} from 'redux/actions';
|
} from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import {
|
import {
|
||||||
getTopicMessgesMeta,
|
getTopicMessgesMeta,
|
||||||
getTopicMessgesPhase,
|
getTopicMessgesPhase,
|
||||||
|
|
|
@ -14,11 +14,11 @@ import { Tag } from 'components/common/Tag/Tag.styled';
|
||||||
export interface Props extends Topic, TopicDetails {
|
export interface Props extends Topic, TopicDetails {
|
||||||
clusterName: ClusterName;
|
clusterName: ClusterName;
|
||||||
topicName: TopicName;
|
topicName: TopicName;
|
||||||
clearTopicMessages(
|
clearTopicMessages(params: {
|
||||||
clusterName: ClusterName,
|
clusterName: ClusterName;
|
||||||
topicName: TopicName,
|
topicName: TopicName;
|
||||||
partitions?: number[]
|
partitions?: number[];
|
||||||
): void;
|
}): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Overview: React.FC<Props> = ({
|
const Overview: React.FC<Props> = ({
|
||||||
|
@ -120,9 +120,11 @@ const Overview: React.FC<Props> = ({
|
||||||
<Dropdown label={<VerticalElipsisIcon />} right>
|
<Dropdown label={<VerticalElipsisIcon />} right>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
clearTopicMessages(clusterName, topicName, [
|
clearTopicMessages({
|
||||||
partition,
|
clusterName,
|
||||||
])
|
topicName,
|
||||||
|
partitions: [partition],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
danger
|
danger
|
||||||
>
|
>
|
||||||
|
|
|
@ -2,7 +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 { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import Overview from 'components/Topics/Topic/Details/Overview/Overview';
|
import Overview from 'components/Topics/Topic/Details/Overview/Overview';
|
||||||
|
|
||||||
interface RouteProps {
|
interface RouteProps {
|
||||||
|
|
|
@ -127,10 +127,10 @@ describe('Details', () => {
|
||||||
const submitButton = screen.getAllByText('Submit')[0];
|
const submitButton = screen.getAllByText('Submit')[0];
|
||||||
userEvent.click(submitButton);
|
userEvent.click(submitButton);
|
||||||
|
|
||||||
expect(mockClearTopicMessages).toHaveBeenCalledWith(
|
expect(mockClearTopicMessages).toHaveBeenCalledWith({
|
||||||
mockClusterName,
|
clusterName: mockClusterName,
|
||||||
internalTopicPayload.name
|
topicName: internalTopicPayload.name,
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes the modal when cancel button is clicked', () => {
|
it('closes the modal when cancel button is clicked', () => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { RootState } from 'redux/interfaces';
|
import { RootState } from 'redux/interfaces';
|
||||||
import { fetchTopicDetails, resetTopicMessages } from 'redux/actions';
|
import { fetchTopicDetails } from 'redux/actions';
|
||||||
|
import { resetTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import { getIsTopicDetailsFetching } from 'redux/reducers/topics/selectors';
|
import { getIsTopicDetailsFetching } from 'redux/reducers/topics/selectors';
|
||||||
|
|
||||||
import Topic from './Topic';
|
import Topic from './Topic';
|
||||||
|
|
|
@ -5,10 +5,6 @@ import {
|
||||||
TopicMessageSchema,
|
TopicMessageSchema,
|
||||||
} from 'generated-sources';
|
} from 'generated-sources';
|
||||||
import { FailurePayload } from 'redux/interfaces';
|
import { FailurePayload } from 'redux/interfaces';
|
||||||
import {
|
|
||||||
topicMessagePayload,
|
|
||||||
topicMessagesMetaPayload,
|
|
||||||
} from 'redux/reducers/topicMessages/__test__/fixtures';
|
|
||||||
|
|
||||||
import { mockTopicsState } from './fixtures';
|
import { mockTopicsState } from './fixtures';
|
||||||
|
|
||||||
|
@ -85,39 +81,6 @@ describe('Actions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('topic messages', () => {
|
|
||||||
it('creates ADD_TOPIC_MESSAGE', () => {
|
|
||||||
expect(actions.addTopicMessage({ message: topicMessagePayload })).toEqual(
|
|
||||||
{
|
|
||||||
type: 'ADD_TOPIC_MESSAGE',
|
|
||||||
payload: { message: topicMessagePayload },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates RESET_TOPIC_MESSAGES', () => {
|
|
||||||
expect(actions.resetTopicMessages()).toEqual({
|
|
||||||
type: 'RESET_TOPIC_MESSAGES',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates UPDATE_TOPIC_MESSAGES_PHASE', () => {
|
|
||||||
expect(actions.updateTopicMessagesPhase('Polling')).toEqual({
|
|
||||||
type: 'UPDATE_TOPIC_MESSAGES_PHASE',
|
|
||||||
payload: 'Polling',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates UPDATE_TOPIC_MESSAGES_META', () => {
|
|
||||||
expect(actions.updateTopicMessagesMeta(topicMessagesMetaPayload)).toEqual(
|
|
||||||
{
|
|
||||||
type: 'UPDATE_TOPIC_MESSAGES_META',
|
|
||||||
payload: topicMessagesMetaPayload,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sending messages', () => {
|
describe('sending messages', () => {
|
||||||
describe('fetchTopicMessageSchemaAction', () => {
|
describe('fetchTopicMessageSchemaAction', () => {
|
||||||
it('creates GET_TOPIC_SCHEMA__REQUEST', () => {
|
it('creates GET_TOPIC_SCHEMA__REQUEST', () => {
|
||||||
|
|
|
@ -83,38 +83,6 @@ 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(getTypeAndPayload(store)).toEqual([
|
|
||||||
actions.clearMessagesTopicAction.request(),
|
|
||||||
actions.clearMessagesTopicAction.success(),
|
|
||||||
...getAlertActions(store),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
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) {
|
|
||||||
const err = error as Response;
|
|
||||||
expect(err.status).toEqual(404);
|
|
||||||
expect(store.getActions()).toEqual([
|
|
||||||
actions.clearMessagesTopicAction.request(),
|
|
||||||
actions.clearMessagesTopicAction.failure({}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('fetchTopicConsumerGroups', () => {
|
describe('fetchTopicConsumerGroups', () => {
|
||||||
it('GET_TOPIC_CONSUMER_GROUPS__FAILURE', async () => {
|
it('GET_TOPIC_CONSUMER_GROUPS__FAILURE', async () => {
|
||||||
fetchMock.getOnce(
|
fetchMock.getOnce(
|
||||||
|
|
|
@ -3,8 +3,6 @@ import { FailurePayload, TopicName, TopicsState } from 'redux/interfaces';
|
||||||
import {
|
import {
|
||||||
TopicColumnsToSort,
|
TopicColumnsToSort,
|
||||||
Topic,
|
Topic,
|
||||||
TopicMessage,
|
|
||||||
TopicMessageConsuming,
|
|
||||||
TopicMessageSchema,
|
TopicMessageSchema,
|
||||||
} from 'generated-sources';
|
} from 'generated-sources';
|
||||||
|
|
||||||
|
@ -73,25 +71,6 @@ export const fetchTopicConsumerGroupsAction = createAsyncAction(
|
||||||
'GET_TOPIC_CONSUMER_GROUPS__FAILURE'
|
'GET_TOPIC_CONSUMER_GROUPS__FAILURE'
|
||||||
)<undefined, TopicsState, undefined>();
|
)<undefined, TopicsState, undefined>();
|
||||||
|
|
||||||
export const addTopicMessage = createAction('ADD_TOPIC_MESSAGE')<{
|
|
||||||
message: TopicMessage;
|
|
||||||
prepend?: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
export const resetTopicMessages = createAction('RESET_TOPIC_MESSAGES')();
|
|
||||||
|
|
||||||
export const setTopicMessagesFetchingStatus = createAction(
|
|
||||||
'SET_TOPIC_MESSAGES_FETCHING_STATUS'
|
|
||||||
)<boolean>();
|
|
||||||
|
|
||||||
export const updateTopicMessagesPhase = createAction(
|
|
||||||
'UPDATE_TOPIC_MESSAGES_PHASE'
|
|
||||||
)<string>();
|
|
||||||
|
|
||||||
export const updateTopicMessagesMeta = createAction(
|
|
||||||
'UPDATE_TOPIC_MESSAGES_META'
|
|
||||||
)<TopicMessageConsuming>();
|
|
||||||
|
|
||||||
export const fetchTopicMessageSchemaAction = createAsyncAction(
|
export const fetchTopicMessageSchemaAction = createAsyncAction(
|
||||||
'GET_TOPIC_SCHEMA__REQUEST',
|
'GET_TOPIC_SCHEMA__REQUEST',
|
||||||
'GET_TOPIC_SCHEMA__SUCCESS',
|
'GET_TOPIC_SCHEMA__SUCCESS',
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
TopicFormData,
|
TopicFormData,
|
||||||
AppDispatch,
|
AppDispatch,
|
||||||
} from 'redux/interfaces';
|
} from 'redux/interfaces';
|
||||||
|
import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import { BASE_PARAMS } from 'lib/constants';
|
import { BASE_PARAMS } from 'lib/constants';
|
||||||
import * as actions from 'redux/actions/actions';
|
import * as actions from 'redux/actions/actions';
|
||||||
import { getResponse } from 'lib/errorHandling';
|
import { getResponse } from 'lib/errorHandling';
|
||||||
|
@ -63,44 +64,12 @@ export const fetchTopicsList =
|
||||||
dispatch(actions.fetchTopicsListAction.failure());
|
dispatch(actions.fetchTopicsListAction.failure());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
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());
|
|
||||||
|
|
||||||
(dispatch as AppDispatch)(
|
|
||||||
showSuccessAlert({
|
|
||||||
id: `message-${topicName}-${clusterName}-${partitions}`,
|
|
||||||
message: 'Messages successfully cleared!',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} 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 clearTopicsMessages =
|
export const clearTopicsMessages =
|
||||||
(clusterName: ClusterName, topicsName: TopicName[]): PromiseThunkResult =>
|
(clusterName: ClusterName, topicsName: TopicName[]): PromiseThunkResult =>
|
||||||
async (dispatch) => {
|
async (dispatch) => {
|
||||||
topicsName.forEach((topicName) => {
|
topicsName.forEach((topicName) => {
|
||||||
dispatch(clearTopicMessages(clusterName, topicName));
|
dispatch(clearTopicMessages({ clusterName, topicName }));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import schemas from 'redux/reducers/schemas/schemasSlice';
|
||||||
import connect from 'redux/reducers/connect/connectSlice';
|
import connect from 'redux/reducers/connect/connectSlice';
|
||||||
|
|
||||||
import topics from './topics/reducer';
|
import topics from './topics/reducer';
|
||||||
import topicMessages from './topicMessages/reducer';
|
import topicMessages from './topicMessages/topicMessagesSlice';
|
||||||
import consumerGroups from './consumerGroups/consumerGroupsSlice';
|
import consumerGroups from './consumerGroups/consumerGroupsSlice';
|
||||||
import ksqlDb from './ksqlDb/ksqlDbSlice';
|
import ksqlDb from './ksqlDb/ksqlDbSlice';
|
||||||
import legacyLoader from './loader/reducer';
|
import legacyLoader from './loader/reducer';
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {
|
import reducer, {
|
||||||
addTopicMessage,
|
addTopicMessage,
|
||||||
|
clearTopicMessages,
|
||||||
resetTopicMessages,
|
resetTopicMessages,
|
||||||
updateTopicMessagesMeta,
|
updateTopicMessagesMeta,
|
||||||
updateTopicMessagesPhase,
|
updateTopicMessagesPhase,
|
||||||
} from 'redux/actions';
|
} from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
import reducer from 'redux/reducers/topicMessages/reducer';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
topicMessagePayload,
|
topicMessagePayload,
|
||||||
|
@ -12,6 +12,9 @@ import {
|
||||||
topicMessagesMetaPayload,
|
topicMessagesMetaPayload,
|
||||||
} from './fixtures';
|
} from './fixtures';
|
||||||
|
|
||||||
|
const clusterName = 'local';
|
||||||
|
const topicName = 'localTopic';
|
||||||
|
|
||||||
describe('TopicMessages reducer', () => {
|
describe('TopicMessages reducer', () => {
|
||||||
it('Adds new message', () => {
|
it('Adds new message', () => {
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
|
@ -53,7 +56,7 @@ describe('TopicMessages reducer', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Clears messages', () => {
|
it('reset messages', () => {
|
||||||
const state = reducer(
|
const state = reducer(
|
||||||
undefined,
|
undefined,
|
||||||
addTopicMessage({ message: topicMessagePayload })
|
addTopicMessage({ message: topicMessagePayload })
|
||||||
|
@ -63,6 +66,25 @@ describe('TopicMessages reducer', () => {
|
||||||
const newState = reducer(state, resetTopicMessages());
|
const newState = reducer(state, resetTopicMessages());
|
||||||
expect(newState.messages.length).toEqual(0);
|
expect(newState.messages.length).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clear messages', () => {
|
||||||
|
const state = reducer(
|
||||||
|
undefined,
|
||||||
|
addTopicMessage({ message: topicMessagePayload })
|
||||||
|
);
|
||||||
|
expect(state.messages.length).toEqual(1);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
reducer(state, {
|
||||||
|
type: clearTopicMessages.fulfilled,
|
||||||
|
payload: { clusterName, topicName },
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
...state,
|
||||||
|
messages: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Updates Topic Messages Phase', () => {
|
it('Updates Topic Messages Phase', () => {
|
||||||
const phase = 'Polling';
|
const phase = 'Polling';
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { store } from 'redux/store';
|
import { store } from 'redux/store';
|
||||||
import * as selectors from 'redux/reducers/topicMessages/selectors';
|
import * as selectors from 'redux/reducers/topicMessages/selectors';
|
||||||
import { initialState } from 'redux/reducers/topicMessages/reducer';
|
|
||||||
import {
|
import {
|
||||||
|
initialState,
|
||||||
addTopicMessage,
|
addTopicMessage,
|
||||||
updateTopicMessagesMeta,
|
updateTopicMessagesMeta,
|
||||||
updateTopicMessagesPhase,
|
updateTopicMessagesPhase,
|
||||||
} from 'redux/actions';
|
} from 'redux/reducers/topicMessages/topicMessagesSlice';
|
||||||
|
|
||||||
import { topicMessagePayload, topicMessagesMetaPayload } from './fixtures';
|
import { topicMessagePayload, topicMessagesMetaPayload } from './fixtures';
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
import { Action, TopicMessagesState } from 'redux/interfaces';
|
|
||||||
import { getType } from 'typesafe-actions';
|
|
||||||
import * as actions from 'redux/actions';
|
|
||||||
import { TopicMessage } from 'generated-sources';
|
|
||||||
|
|
||||||
export const initialState: TopicMessagesState = {
|
|
||||||
messages: [],
|
|
||||||
meta: {
|
|
||||||
bytesConsumed: 0,
|
|
||||||
elapsedMs: 0,
|
|
||||||
messagesConsumed: 0,
|
|
||||||
isCancelled: false,
|
|
||||||
},
|
|
||||||
isFetching: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/default-param-last
|
|
||||||
const reducer = (state = initialState, action: Action): TopicMessagesState => {
|
|
||||||
switch (action.type) {
|
|
||||||
case getType(actions.addTopicMessage): {
|
|
||||||
const messages: TopicMessage[] = action.payload.prepend
|
|
||||||
? [action.payload.message, ...state.messages]
|
|
||||||
: [...state.messages, action.payload.message];
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
messages,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case getType(actions.resetTopicMessages):
|
|
||||||
return initialState;
|
|
||||||
case getType(actions.updateTopicMessagesPhase):
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
phase: action.payload,
|
|
||||||
};
|
|
||||||
case getType(actions.updateTopicMessagesMeta):
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
meta: action.payload,
|
|
||||||
};
|
|
||||||
case getType(actions.setTopicMessagesFetchingStatus):
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
isFetching: action.payload,
|
|
||||||
};
|
|
||||||
case getType(actions.clearMessagesTopicAction.success):
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
messages: [],
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reducer;
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||||
|
import { TopicMessagesState, ClusterName, TopicName } from 'redux/interfaces';
|
||||||
|
import { TopicMessage, Configuration, MessagesApi } from 'generated-sources';
|
||||||
|
import { BASE_PARAMS } from 'lib/constants';
|
||||||
|
import { getResponse } from 'lib/errorHandling';
|
||||||
|
import { showSuccessAlert } from 'redux/reducers/alerts/alertsSlice';
|
||||||
|
|
||||||
|
const apiClientConf = new Configuration(BASE_PARAMS);
|
||||||
|
export const messagesApiClient = new MessagesApi(apiClientConf);
|
||||||
|
|
||||||
|
export const clearTopicMessages = createAsyncThunk<
|
||||||
|
undefined,
|
||||||
|
{ clusterName: ClusterName; topicName: TopicName; partitions?: number[] }
|
||||||
|
>(
|
||||||
|
'topicMessages/clearTopicMessages',
|
||||||
|
async (
|
||||||
|
{ clusterName, topicName, partitions },
|
||||||
|
{ rejectWithValue, dispatch }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
await messagesApiClient.deleteTopicMessages({
|
||||||
|
clusterName,
|
||||||
|
topicName,
|
||||||
|
partitions,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
showSuccessAlert({
|
||||||
|
id: `message-${topicName}-${clusterName}-${partitions}`,
|
||||||
|
message: 'Messages successfully cleared!',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
} catch (err) {
|
||||||
|
return rejectWithValue(await getResponse(err as Response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const initialState: TopicMessagesState = {
|
||||||
|
messages: [],
|
||||||
|
meta: {
|
||||||
|
bytesConsumed: 0,
|
||||||
|
elapsedMs: 0,
|
||||||
|
messagesConsumed: 0,
|
||||||
|
isCancelled: false,
|
||||||
|
},
|
||||||
|
isFetching: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const topicMessagesSlice = createSlice({
|
||||||
|
name: 'topicMessages',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
addTopicMessage: (state, action) => {
|
||||||
|
const messages: TopicMessage[] = action.payload.prepend
|
||||||
|
? [action.payload.message, ...state.messages]
|
||||||
|
: [...state.messages, action.payload.message];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
messages,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
resetTopicMessages: () => initialState,
|
||||||
|
updateTopicMessagesPhase: (state, action) => {
|
||||||
|
state.phase = action.payload;
|
||||||
|
},
|
||||||
|
updateTopicMessagesMeta: (state, action) => {
|
||||||
|
state.meta = action.payload;
|
||||||
|
},
|
||||||
|
setTopicMessagesFetchingStatus: (state, action) => {
|
||||||
|
state.isFetching = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(clearTopicMessages.fulfilled, (state) => {
|
||||||
|
state.messages = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
addTopicMessage,
|
||||||
|
resetTopicMessages,
|
||||||
|
updateTopicMessagesPhase,
|
||||||
|
updateTopicMessagesMeta,
|
||||||
|
setTopicMessagesFetchingStatus,
|
||||||
|
} = topicMessagesSlice.actions;
|
||||||
|
|
||||||
|
export default topicMessagesSlice.reducer;
|
Loading…
Add table
Reference in a new issue