Repository: provectus/kafka-ui
Issue: The Message count is not updating with Clearing message for partition 
Labels: type/bug
good first issue
scope/frontend
status/accepted
status/confirmed

**Describe the bug**
Message count stays the same after clearing message for partition in Topic's overview page.
Needs refreshing the page.

**Set up**
https://www.kafka-ui.provectus.io/


**Steps to Reproduce**

1. Navigate to Topic's overview 
2. Open the menu for partition
3. Press Clear Messages


**Expected behavior**
Messages count should be updated immediately

**Screenshots**

https://user-images.githubusercontent.com/104780608/196624761-0847ba39-4c30-4562-8890-31af9ec985c8.mov




**Additional context**
<!--
(Add any other context about the problem here)
-->


Issue comments:
Hey, can i be assigned to this
@Drex72 any luck?
@Haarolean this can be checked by QA
Bug still exists on master, let's make status ready after merging.
Will test related PR https://github.com/provectus/kafka-ui/pull/3080

Pull request review comments:
i think it is best to transform this `redux` functionality clear -> `react-query` , hence the `query` will handle delete as well, this way it is mixed.
it is still un-removed here , we should change it like the upper one.
i done it 
`clearMessage` or `clearMessages`
i change it 
this whole file can be deleted as well, since it serves no use , `state.isFetching` is controlled by query hook, so that already gives `isLoading`.
should i remove whole topicMessages reducer ?
because if I delete TopicMessagesState, then it will be necessary to change a lot of files and tests

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx b/kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx
index 4fb5f26c7d2..2f6fe3f49af 100644
--- a/kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx
+++ b/kafka-ui-react-app/src/components/Topics/List/ActionsCell.tsx
@@ -1,20 +1,17 @@
 import React from 'react';
 import { Action, CleanUpPolicy, Topic, ResourceType } from 'generated-sources';
 import { CellContext } from '@tanstack/react-table';
-import { useAppDispatch } from 'lib/hooks/redux';
 import ClusterContext from 'components/contexts/ClusterContext';
 import { ClusterNameRoute } from 'lib/paths';
 import useAppParams from 'lib/hooks/useAppParams';
-import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import {
   Dropdown,
   DropdownItem,
   DropdownItemHint,
 } from 'components/common/Dropdown';
-import { useQueryClient } from '@tanstack/react-query';
 import {
-  topicKeys,
   useDeleteTopic,
+  useClearTopicMessages,
   useRecreateTopic,
 } from 'lib/hooks/api/topics';
 import { ActionDropdownItem } from 'components/common/ActionComponent';
@@ -24,20 +21,16 @@ const ActionsCell: React.FC<CellContext<Topic, unknown>> = ({ row }) => {
 
   const { isReadOnly, isTopicDeletionAllowed } =
     React.useContext(ClusterContext);
-  const dispatch = useAppDispatch();
   const { clusterName } = useAppParams<ClusterNameRoute>();
-  const queryClient = useQueryClient();
 
+  const clearMessages = useClearTopicMessages(clusterName);
   const deleteTopic = useDeleteTopic(clusterName);
   const recreateTopic = useRecreateTopic({ clusterName, topicName: name });
 
   const disabled = internal || isReadOnly;
 
   const clearTopicMessagesHandler = async () => {
-    await dispatch(
-      clearTopicMessages({ clusterName, topicName: name })
-    ).unwrap();
-    return queryClient.invalidateQueries(topicKeys.all(clusterName));
+    await clearMessages.mutateAsync(name);
   };
 
   const isCleanupDisabled = cleanUpPolicy !== CleanUpPolicy.DELETE;
diff --git a/kafka-ui-react-app/src/components/Topics/List/BatchActionsBar.tsx b/kafka-ui-react-app/src/components/Topics/List/BatchActionsBar.tsx
index d75f4d0da2c..a04429be738 100644
--- a/kafka-ui-react-app/src/components/Topics/List/BatchActionsBar.tsx
+++ b/kafka-ui-react-app/src/components/Topics/List/BatchActionsBar.tsx
@@ -3,11 +3,13 @@ import { Row } from '@tanstack/react-table';
 import { Action, Topic, ResourceType } from 'generated-sources';
 import useAppParams from 'lib/hooks/useAppParams';
 import { ClusterName } from 'redux/interfaces';
-import { topicKeys, useDeleteTopic } from 'lib/hooks/api/topics';
+import {
+  topicKeys,
+  useClearTopicMessages,
+  useDeleteTopic,
+} from 'lib/hooks/api/topics';
 import { useConfirm } from 'lib/hooks/useConfirm';
 import { Button } from 'components/common/Button/Button';
-import { useAppDispatch } from 'lib/hooks/redux';
-import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import { clusterTopicCopyRelativePath } from 'lib/paths';
 import { useQueryClient } from '@tanstack/react-query';
 import { ActionCanButton } from 'components/common/ActionComponent';
@@ -25,11 +27,14 @@ const BatchActionsbar: React.FC<BatchActionsbarProps> = ({
 }) => {
   const { clusterName } = useAppParams<{ clusterName: ClusterName }>();
   const confirm = useConfirm();
-  const dispatch = useAppDispatch();
   const deleteTopic = useDeleteTopic(clusterName);
   const selectedTopics = rows.map(({ original }) => original.name);
   const client = useQueryClient();
 
+  const clearMessages = useClearTopicMessages(clusterName);
+  const clearTopicMessagesHandler = async (topicName: Topic['name']) => {
+    await clearMessages.mutateAsync(topicName);
+  };
   const deleteTopicsHandler = () => {
     confirm('Are you sure you want to remove selected topics?', async () => {
       try {
@@ -50,7 +55,7 @@ const BatchActionsbar: React.FC<BatchActionsbarProps> = ({
         try {
           await Promise.all(
             selectedTopics.map((topicName) =>
-              dispatch(clearTopicMessages({ clusterName, topicName })).unwrap()
+              clearTopicMessagesHandler(topicName)
             )
           );
           resetRowSelection();
diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
index 5e494cdfc8f..61be3f1360c 100644
--- a/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
+++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
@@ -6,16 +6,15 @@ import { externalTopicPayload, topicsPayload } from 'lib/fixtures/topics';
 import ClusterContext from 'components/contexts/ClusterContext';
 import userEvent from '@testing-library/user-event';
 import {
+  useClearTopicMessages,
   useDeleteTopic,
   useRecreateTopic,
   useTopics,
 } from 'lib/hooks/api/topics';
 import TopicTable from 'components/Topics/List/TopicTable';
 import { clusterTopicsPath } from 'lib/paths';
-import { useAppDispatch } from 'lib/hooks/redux';
 
 const clusterName = 'test-cluster';
-const unwrapMock = jest.fn();
 
 jest.mock('lib/hooks/redux', () => ({
   ...jest.requireActual('lib/hooks/redux'),
@@ -29,22 +28,24 @@ jest.mock('lib/hooks/api/topics', () => ({
   useDeleteTopic: jest.fn(),
   useRecreateTopic: jest.fn(),
   useTopics: jest.fn(),
+  useClearTopicMessages: jest.fn(),
 }));
 
 const deleteTopicMock = jest.fn();
 const recreateTopicMock = jest.fn();
+const clearTopicMessages = jest.fn();
 
 describe('TopicTable Components', () => {
   beforeEach(() => {
     (useDeleteTopic as jest.Mock).mockImplementation(() => ({
       mutateAsync: deleteTopicMock,
     }));
+    (useClearTopicMessages as jest.Mock).mockImplementation(() => ({
+      mutateAsync: clearTopicMessages,
+    }));
     (useRecreateTopic as jest.Mock).mockImplementation(() => ({
       mutateAsync: recreateTopicMock,
     }));
-    (useAppDispatch as jest.Mock).mockImplementation(() => () => ({
-      unwrap: unwrapMock,
-    }));
   });
 
   const renderComponent = (
@@ -185,9 +186,9 @@ describe('TopicTable Components', () => {
             ).toBeInTheDocument();
             const confirmBtn = getButtonByName('Confirm');
             expect(confirmBtn).toBeInTheDocument();
-            expect(unwrapMock).not.toHaveBeenCalled();
+            expect(clearTopicMessages).not.toHaveBeenCalled();
             await userEvent.click(confirmBtn);
-            expect(unwrapMock).toHaveBeenCalledTimes(2);
+            expect(clearTopicMessages).toHaveBeenCalledTimes(2);
             expect(screen.getAllByRole('checkbox')[1]).not.toBeChecked();
             expect(screen.getAllByRole('checkbox')[2]).not.toBeChecked();
           });
@@ -282,7 +283,7 @@ describe('TopicTable Components', () => {
           await userEvent.click(
             screen.getByRole('button', { name: 'Confirm' })
           );
-          expect(unwrapMock).toHaveBeenCalled();
+          expect(clearTopicMessages).toHaveBeenCalled();
         });
       });
 
diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Overview/ActionsCell.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Overview/ActionsCell.tsx
index 0e1eed205a9..19e03a1411c 100644
--- a/kafka-ui-react-app/src/components/Topics/Topic/Overview/ActionsCell.tsx
+++ b/kafka-ui-react-app/src/components/Topics/Topic/Overview/ActionsCell.tsx
@@ -1,13 +1,11 @@
 import React from 'react';
 import { Action, Partition, ResourceType } from 'generated-sources';
 import { CellContext } from '@tanstack/react-table';
-import { useAppDispatch } from 'lib/hooks/redux';
 import ClusterContext from 'components/contexts/ClusterContext';
 import { RouteParamsClusterTopic } from 'lib/paths';
 import useAppParams from 'lib/hooks/useAppParams';
-import { clearTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import { Dropdown } from 'components/common/Dropdown';
-import { useTopicDetails } from 'lib/hooks/api/topics';
+import { useClearTopicMessages, useTopicDetails } from 'lib/hooks/api/topics';
 import { ActionDropdownItem } from 'components/common/ActionComponent';
 
 const ActionsCell: React.FC<CellContext<Partition, unknown>> = ({ row }) => {
@@ -15,12 +13,11 @@ const ActionsCell: React.FC<CellContext<Partition, unknown>> = ({ row }) => {
   const { data } = useTopicDetails({ clusterName, topicName });
   const { isReadOnly } = React.useContext(ClusterContext);
   const { partition } = row.original;
-  const dispatch = useAppDispatch();
+
+  const clearMessages = useClearTopicMessages(clusterName, [partition]);
 
   const clearTopicMessagesHandler = async () => {
-    await dispatch(
-      clearTopicMessages({ clusterName, topicName, partitions: [partition] })
-    ).unwrap();
+    await clearMessages.mutateAsync(topicName);
   };
   const disabled =
     data?.internal || isReadOnly || data?.cleanUpPolicy !== 'DELETE';
diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx
index da05b108c73..bf762a002b3 100644
--- a/kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx
+++ b/kafka-ui-react-app/src/components/Topics/Topic/Overview/__test__/Overview.spec.tsx
@@ -8,8 +8,7 @@ import ClusterContext from 'components/contexts/ClusterContext';
 import userEvent from '@testing-library/user-event';
 import { clusterTopicPath } from 'lib/paths';
 import { Replica } from 'components/Topics/Topic/Overview/Overview.styled';
-import { useTopicDetails } from 'lib/hooks/api/topics';
-import { useAppDispatch } from 'lib/hooks/redux';
+import { useClearTopicMessages, useTopicDetails } from 'lib/hooks/api/topics';
 import {
   externalTopicPayload,
   internalTopicPayload,
@@ -26,14 +25,10 @@ const defaultContextValues = {
 
 jest.mock('lib/hooks/api/topics', () => ({
   useTopicDetails: jest.fn(),
+  useClearTopicMessages: jest.fn(),
 }));
 
-const unwrapMock = jest.fn();
-
-jest.mock('lib/hooks/redux', () => ({
-  ...jest.requireActual('lib/hooks/redux'),
-  useAppDispatch: jest.fn(),
-}));
+const clearTopicMessage = jest.fn();
 
 describe('Overview', () => {
   const renderComponent = (
@@ -43,6 +38,9 @@ describe('Overview', () => {
     (useTopicDetails as jest.Mock).mockImplementation(() => ({
       data: topic,
     }));
+    (useClearTopicMessages as jest.Mock).mockImplementation(() => ({
+      mutateAsync: clearTopicMessage,
+    }));
     const path = clusterTopicPath(clusterName, topicName);
     return render(
       <WithRoute path={clusterTopicPath()}>
@@ -54,12 +52,6 @@ describe('Overview', () => {
     );
   };
 
-  beforeEach(() => {
-    (useAppDispatch as jest.Mock).mockImplementation(() => () => ({
-      unwrap: unwrapMock,
-    }));
-  });
-
   it('at least one replica was rendered', () => {
     renderComponent();
     expect(screen.getByLabelText('replica-info')).toBeInTheDocument();
@@ -136,7 +128,7 @@ describe('Overview', () => {
 
       const clearMessagesButton = screen.getByText('Clear Messages');
       await userEvent.click(clearMessagesButton);
-      expect(unwrapMock).toHaveBeenCalledTimes(1);
+      expect(clearTopicMessage).toHaveBeenCalledTimes(1);
     });
   });
 
diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx
index 01e140c8bb0..8945523576b 100644
--- a/kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx
+++ b/kafka-ui-react-app/src/components/Topics/Topic/Topic.tsx
@@ -21,14 +21,12 @@ import { useAppDispatch } from 'lib/hooks/redux';
 import useAppParams from 'lib/hooks/useAppParams';
 import { Dropdown, DropdownItemHint } from 'components/common/Dropdown';
 import {
+  useClearTopicMessages,
   useDeleteTopic,
   useRecreateTopic,
   useTopicDetails,
 } from 'lib/hooks/api/topics';
-import {
-  clearTopicMessages,
-  resetTopicMessages,
-} from 'redux/reducers/topicMessages/topicMessagesSlice';
+import { resetTopicMessages } from 'redux/reducers/topicMessages/topicMessagesSlice';
 import { Action, CleanUpPolicy, ResourceType } from 'generated-sources';
 import PageLoader from 'components/common/PageLoader/PageLoader';
 import SlidingSidebar from 'components/common/SlidingSidebar';
@@ -69,9 +67,11 @@ const Topic: React.FC = () => {
       dispatch(resetTopicMessages());
     };
   }, []);
-
+  const clearMessages = useClearTopicMessages(clusterName);
+  const clearTopicMessagesHandler = async () => {
+    await clearMessages.mutateAsync(topicName);
+  };
   const canCleanup = data?.cleanUpPolicy === CleanUpPolicy.DELETE;
-
   return (
     <>
       <PageHeading
@@ -110,9 +110,7 @@ const Topic: React.FC = () => {
           </ActionDropdownItem>
 
           <ActionDropdownItem
-            onClick={() =>
-              dispatch(clearTopicMessages({ clusterName, topicName })).unwrap()
-            }
+            onClick={clearTopicMessagesHandler}
             confirm="Are you sure want to clear topic messages?"
             disabled={!canCleanup}
             danger
diff --git a/kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx b/kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx
index a686b38e2ba..460e4ad5dee 100644
--- a/kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx
+++ b/kafka-ui-react-app/src/components/Topics/Topic/__test__/Topic.spec.tsx
@@ -16,6 +16,7 @@ import {
 import { CleanUpPolicy, Topic } from 'generated-sources';
 import { externalTopicPayload } from 'lib/fixtures/topics';
 import {
+  useClearTopicMessages,
   useDeleteTopic,
   useRecreateTopic,
   useTopicDetails,
@@ -31,9 +32,11 @@ jest.mock('lib/hooks/api/topics', () => ({
   useTopicDetails: jest.fn(),
   useDeleteTopic: jest.fn(),
   useRecreateTopic: jest.fn(),
+  useClearTopicMessages: jest.fn(),
 }));
 
 const unwrapMock = jest.fn();
+const clearTopicMessages = jest.fn();
 
 jest.mock('lib/hooks/redux', () => ({
   ...jest.requireActual('lib/hooks/redux'),
@@ -98,6 +101,9 @@ describe('Details', () => {
     (useRecreateTopic as jest.Mock).mockImplementation(() => ({
       mutateAsync: mockRecreate,
     }));
+    (useClearTopicMessages as jest.Mock).mockImplementation(() => ({
+      mutateAsync: clearTopicMessages,
+    }));
     (useAppDispatch as jest.Mock).mockImplementation(() => () => ({
       unwrap: unwrapMock,
     }));
@@ -145,7 +151,7 @@ describe('Details', () => {
           name: 'Confirm',
         })[0];
         await waitFor(() => userEvent.click(submitButton));
-        expect(unwrapMock).toHaveBeenCalledTimes(1);
+        expect(clearTopicMessages).toHaveBeenCalledTimes(1);
       });
 
       it('closes the modal when cancel button is clicked', async () => {
diff --git a/kafka-ui-react-app/src/lib/hooks/api/topics.ts b/kafka-ui-react-app/src/lib/hooks/api/topics.ts
index 9af0b63d620..23e6d65ce37 100644
--- a/kafka-ui-react-app/src/lib/hooks/api/topics.ts
+++ b/kafka-ui-react-app/src/lib/hooks/api/topics.ts
@@ -2,6 +2,7 @@ import {
   topicsApiClient as api,
   messagesApiClient as messagesApi,
   consumerGroupsApiClient,
+  messagesApiClient,
 } from 'lib/api';
 import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
 import {
@@ -233,6 +234,34 @@ export function useDeleteTopic(clusterName: ClusterName) {
     }
   );
 }
+
+export function useClearTopicMessages(
+  clusterName: ClusterName,
+  partitions?: number[]
+) {
+  const client = useQueryClient();
+  return useMutation(
+    async (topicName: Topic['name']) => {
+      await messagesApiClient.deleteTopicMessages({
+        clusterName,
+        partitions,
+        topicName,
+      });
+      return topicName;
+    },
+
+    {
+      onSuccess: (topicName) => {
+        showSuccessAlert({
+          id: `message-${topicName}-${clusterName}-${partitions}`,
+          message: `${topicName} messages have been successfully cleared!`,
+        });
+        client.invalidateQueries(topicKeys.all(clusterName));
+      },
+    }
+  );
+}
+
 export function useRecreateTopic(props: GetTopicDetailsRequest) {
   const client = useQueryClient();
   return useMutation(() => api.recreateTopic(props), {
diff --git a/kafka-ui-react-app/src/redux/reducers/topicMessages/__test__/reducer.spec.ts b/kafka-ui-react-app/src/redux/reducers/topicMessages/__test__/reducer.spec.ts
index c2cfa73f273..b9fa4cd4ec1 100644
--- a/kafka-ui-react-app/src/redux/reducers/topicMessages/__test__/reducer.spec.ts
+++ b/kafka-ui-react-app/src/redux/reducers/topicMessages/__test__/reducer.spec.ts
@@ -1,6 +1,5 @@
 import reducer, {
   addTopicMessage,
-  clearTopicMessages,
   resetTopicMessages,
   updateTopicMessagesMeta,
   updateTopicMessagesPhase,
@@ -12,9 +11,6 @@ import {
   topicMessagesMetaPayload,
 } from './fixtures';
 
-const clusterName = 'local';
-const topicName = 'localTopic';
-
 describe('TopicMessages reducer', () => {
   it('Adds new message', () => {
     const state = reducer(
@@ -67,24 +63,6 @@ describe('TopicMessages reducer', () => {
     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', () => {
     const phase = 'Polling';
 
diff --git a/kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts b/kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts
index 5de97980f31..846cbd0c978 100644
--- a/kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts
+++ b/kafka-ui-react-app/src/redux/reducers/topicMessages/topicMessagesSlice.ts
@@ -1,36 +1,6 @@
-import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
-import { TopicMessagesState, ClusterName, TopicName } from 'redux/interfaces';
+import { createSlice } from '@reduxjs/toolkit';
+import { TopicMessagesState } from 'redux/interfaces';
 import { TopicMessage } from 'generated-sources';
-import {
-  getResponse,
-  showServerError,
-  showSuccessAlert,
-} from 'lib/errorHandling';
-import { messagesApiClient } from 'lib/api';
-
-export const clearTopicMessages = createAsyncThunk<
-  undefined,
-  { clusterName: ClusterName; topicName: TopicName; partitions?: number[] }
->(
-  'topicMessages/clearTopicMessages',
-  async ({ clusterName, topicName, partitions }, { rejectWithValue }) => {
-    try {
-      await messagesApiClient.deleteTopicMessages({
-        clusterName,
-        topicName,
-        partitions,
-      });
-      showSuccessAlert({
-        id: `message-${topicName}-${clusterName}-${partitions}`,
-        message: `${topicName} messages have been successfully cleared!`,
-      });
-      return undefined;
-    } catch (err) {
-      showServerError(err as Response);
-      return rejectWithValue(await getResponse(err as Response));
-    }
-  }
-);
 
 export const initialState: TopicMessagesState = {
   messages: [],
@@ -68,11 +38,6 @@ const topicMessagesSlice = createSlice({
       state.isFetching = action.payload;
     },
   },
-  extraReducers: (builder) => {
-    builder.addCase(clearTopicMessages.fulfilled, (state) => {
-      state.messages = [];
-    });
-  },
 });
 
 export const {


Test patch: