From 9971ab32cbd2a7c445b87fc5321b77b4b07476a0 Mon Sep 17 00:00:00 2001 From: "H@di" Date: Mon, 16 Sep 2024 18:49:24 +0330 Subject: [PATCH 01/12] FE: Refactor topic messages next page codebase (#550) --- .../Topics/Topic/Messages/MessagesTable.tsx | 6 +++--- frontend/src/lib/hooks/api/topicMessages.tsx | 14 +++++-------- frontend/src/lib/hooks/useMessagesFilters.ts | 21 +++++++------------ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx index 813cabcfa..c0fb72c08 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx @@ -5,7 +5,7 @@ import { TopicMessage } from 'generated-sources'; import React, { useState } from 'react'; import { Button } from 'components/common/Button/Button'; import * as S from 'components/common/NewTable/Table.styled'; -import { usePaginateTopics, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; +import { useGoToNextPage, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore'; import PreviewModal from './PreviewModal'; @@ -20,7 +20,7 @@ const MessagesTable: React.FC = ({ messages, isFetching, }) => { - const paginate = usePaginateTopics(); + const goToNextPage = useGoToNextPage(); const [previewFor, setPreviewFor] = useState(null); const [keyFilters, setKeyFilters] = useState([]); @@ -101,7 +101,7 @@ const MessagesTable: React.FC = ({ disabled={isLive || isFetching || !nextCursor} buttonType="secondary" buttonSize="L" - onClick={paginate} + onClick={goToNextPage} > Next → diff --git a/frontend/src/lib/hooks/api/topicMessages.tsx b/frontend/src/lib/hooks/api/topicMessages.tsx index ec407c901..adcaed85a 100644 --- a/frontend/src/lib/hooks/api/topicMessages.tsx +++ b/frontend/src/lib/hooks/api/topicMessages.tsx @@ -14,7 +14,7 @@ import { useMutation, useQuery } from '@tanstack/react-query'; import { messagesApiClient } from 'lib/api'; import { useSearchParams } from 'react-router-dom'; import { - getCursorValue, + getPageValue, MessagesFilterKeys, } from 'lib/hooks/useMessagesFilters'; import { convertStrToPollingMode } from 'lib/hooks/filterUtils'; @@ -38,7 +38,7 @@ export const useTopicMessages = ({ React.useState(); const [isFetching, setIsFetching] = React.useState(false); const abortController = useRef(new AbortController()); - const prevCursor = useRef(0); + const currentPage = useRef(1); // get initial properties @@ -103,17 +103,13 @@ export const useTopicMessages = ({ } const { nextCursor, setNextCursor } = useMessageFiltersStore.getState(); - const tempCompareUrl = new URLSearchParams(requestParams); - tempCompareUrl.delete(MessagesFilterKeys.cursor); - const currentCursor = getCursorValue(searchParams); - - // filters stay the same and we have cursor set cursor - if (nextCursor && prevCursor.current < currentCursor) { + const searchParamPage = getPageValue(searchParams); + if (currentPage.current < searchParamPage && nextCursor) { requestParams.set(MessagesFilterKeys.cursor, nextCursor); } + currentPage.current = searchParamPage; - prevCursor.current = currentCursor; await fetchEventSource(`${url}?${requestParams.toString()}`, { method: 'GET', signal: abortController.current.signal, diff --git a/frontend/src/lib/hooks/useMessagesFilters.ts b/frontend/src/lib/hooks/useMessagesFilters.ts index c72a91b49..e7089b889 100644 --- a/frontend/src/lib/hooks/useMessagesFilters.ts +++ b/frontend/src/lib/hooks/useMessagesFilters.ts @@ -28,6 +28,7 @@ export const MessagesFilterKeys = { activeFilterId: 'activeFilterId', activeFilterNPId: 'activeFilterNPId', // not persisted filter name to indicate the refresh cursor: 'cursor', + page: 'page', r: 'r', // used tp force refresh of the data } as const; @@ -52,28 +53,22 @@ export function useRefreshData(initSearchParams?: URLSearchParams) { }; } -export function getCursorValue(urlSearchParam: URLSearchParams) { - const cursor = parseInt( - urlSearchParam.get(MessagesFilterKeys.cursor) || '0', - 10 - ); +export function getPageValue(urlSearchParam: URLSearchParams) { + const page = parseInt(urlSearchParam.get(MessagesFilterKeys.page) || '1', 10); - if (Number.isNaN(cursor)) { + if (Number.isNaN(page)) { return 0; } - return cursor; + return page; } -export function usePaginateTopics(initSearchParams?: URLSearchParams) { +export function useGoToNextPage(initSearchParams?: URLSearchParams) { const [, setSearchParams] = useSearchParams(initSearchParams); return () => { setSearchParams((params) => { - const cursor = getCursorValue(params) + 1; - - if (cursor) { - params.set(MessagesFilterKeys.cursor, cursor.toString()); - } + const nextPage = getPageValue(params) + 1; + params.set(MessagesFilterKeys.page, nextPage.toString()); return params; }); From fa615f37f2878b2e142b544ab45e518b6c63d844 Mon Sep 17 00:00:00 2001 From: "H@di" Date: Mon, 16 Sep 2024 19:43:12 +0330 Subject: [PATCH 02/12] FE: Rename cursor to nextCursor in topic message response (#550) --- .../java/io/kafbat/ui/emitter/ConsumingStats.java | 6 +++--- .../io/kafbat/ui/service/MessagesServiceTest.java | 6 +++--- .../src/main/resources/swagger/kafbat-ui-api.yaml | 6 +++--- frontend/src/lib/hooks/api/topicMessages.tsx | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java b/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java index 6287a9903..a2c8d94f3 100644 --- a/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java +++ b/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java @@ -2,7 +2,7 @@ import io.kafbat.ui.model.TopicMessageConsumingDTO; import io.kafbat.ui.model.TopicMessageEventDTO; -import io.kafbat.ui.model.TopicMessageNextPageCursorDTO; +import io.kafbat.ui.model.TopicMessagePageCursorDTO; import javax.annotation.Nullable; import reactor.core.publisher.FluxSink; @@ -32,9 +32,9 @@ void sendFinishEvent(FluxSink sink, @Nullable Cursor.Track sink.next( new TopicMessageEventDTO() .type(TopicMessageEventDTO.TypeEnum.DONE) - .cursor( + .nextCursor( cursor != null - ? new TopicMessageNextPageCursorDTO().id(cursor.registerCursor()) + ? new TopicMessagePageCursorDTO().id(cursor.registerCursor()) : null ) .consuming(createConsumingStats()) diff --git a/api/src/test/java/io/kafbat/ui/service/MessagesServiceTest.java b/api/src/test/java/io/kafbat/ui/service/MessagesServiceTest.java index 8939b50c3..06dc8e056 100644 --- a/api/src/test/java/io/kafbat/ui/service/MessagesServiceTest.java +++ b/api/src/test/java/io/kafbat/ui/service/MessagesServiceTest.java @@ -131,8 +131,8 @@ void cursorIsRegisteredAfterPollingIsDoneAndCanBeUsedForNextPagePolling(PollingM null, null, pageSize, StringSerde.name(), StringSerde.name()) .doOnNext(evt -> { if (evt.getType() == TopicMessageEventDTO.TypeEnum.DONE) { - assertThat(evt.getCursor()).isNotNull(); - cursorIdCatcher.set(evt.getCursor().getId()); + assertThat(evt.getNextCursor()).isNotNull(); + cursorIdCatcher.set(evt.getNextCursor().getId()); } }) .filter(evt -> evt.getType() == TopicMessageEventDTO.TypeEnum.MESSAGE) @@ -147,7 +147,7 @@ void cursorIsRegisteredAfterPollingIsDoneAndCanBeUsedForNextPagePolling(PollingM Flux remainingMsgs = messagesService.loadMessages(cluster, testTopic, cursorIdCatcher.get()) .doOnNext(evt -> { if (evt.getType() == TopicMessageEventDTO.TypeEnum.DONE) { - assertThat(evt.getCursor()).isNull(); + assertThat(evt.getNextCursor()).isNull(); } }) .filter(evt -> evt.getType() == TopicMessageEventDTO.TypeEnum.MESSAGE) diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 7ca62831f..53b6fc5ea 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -2954,8 +2954,8 @@ components: $ref: "#/components/schemas/TopicMessagePhase" consuming: $ref: "#/components/schemas/TopicMessageConsuming" - cursor: - $ref: "#/components/schemas/TopicMessageNextPageCursor" + nextCursor: + $ref: "#/components/schemas/TopicMessagePageCursor" TopicMessagePhase: type: object @@ -2985,7 +2985,7 @@ components: filterApplyErrors: type: integer - TopicMessageNextPageCursor: + TopicMessagePageCursor: type: object properties: id: diff --git a/frontend/src/lib/hooks/api/topicMessages.tsx b/frontend/src/lib/hooks/api/topicMessages.tsx index adcaed85a..60621df1f 100644 --- a/frontend/src/lib/hooks/api/topicMessages.tsx +++ b/frontend/src/lib/hooks/api/topicMessages.tsx @@ -103,7 +103,6 @@ export const useTopicMessages = ({ } const { nextCursor, setNextCursor } = useMessageFiltersStore.getState(); - const searchParamPage = getPageValue(searchParams); if (currentPage.current < searchParamPage && nextCursor) { requestParams.set(MessagesFilterKeys.cursor, nextCursor); @@ -125,11 +124,7 @@ export const useTopicMessages = ({ }, onmessage(event) { const parsedData: TopicMessageEvent = JSON.parse(event.data); - const { message, consuming, cursor } = parsedData; - - if (useMessageFiltersStore.getState().nextCursor !== cursor?.id) { - setNextCursor(cursor?.id || undefined); - } + const { message, consuming } = parsedData; switch (parsedData.type) { case TopicMessageEventTypeEnum.MESSAGE: @@ -148,6 +143,11 @@ export const useTopicMessages = ({ case TopicMessageEventTypeEnum.CONSUMING: if (consuming) setConsumptionStats(consuming); break; + case TopicMessageEventTypeEnum.DONE: + if (nextCursor !== parsedData.nextCursor?.id) { + setNextCursor(parsedData.nextCursor?.id || undefined); + } + break; default: } }, From 97c5eac4b7fcda3cb981ab3feb08916232160153 Mon Sep 17 00:00:00 2001 From: "H@di" Date: Mon, 16 Sep 2024 20:51:03 +0330 Subject: [PATCH 03/12] FE: Add Prevoius button to topic message page - frontend side (#550) --- .../java/io/kafbat/ui/emitter/ConsumingStats.java | 3 +++ .../src/main/resources/swagger/kafbat-ui-api.yaml | 2 ++ .../Topics/Topic/Messages/MessagesTable.tsx | 12 +++++++++++- frontend/src/lib/hooks/api/topicMessages.tsx | 7 +++++++ frontend/src/lib/hooks/useMessageFiltersStore.ts | 4 ++++ frontend/src/lib/hooks/useMessagesFilters.ts | 12 ++++++++++++ 6 files changed, 39 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java b/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java index a2c8d94f3..27ceb43b6 100644 --- a/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java +++ b/api/src/main/java/io/kafbat/ui/emitter/ConsumingStats.java @@ -32,6 +32,9 @@ void sendFinishEvent(FluxSink sink, @Nullable Cursor.Track sink.next( new TopicMessageEventDTO() .type(TopicMessageEventDTO.TypeEnum.DONE) + .prevCursor( // FIXME + null + ) .nextCursor( cursor != null ? new TopicMessagePageCursorDTO().id(cursor.registerCursor()) diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 53b6fc5ea..1e64dc3b7 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -2954,6 +2954,8 @@ components: $ref: "#/components/schemas/TopicMessagePhase" consuming: $ref: "#/components/schemas/TopicMessageConsuming" + prevCursor: + $ref: "#/components/schemas/TopicMessagePageCursor" nextCursor: $ref: "#/components/schemas/TopicMessagePageCursor" diff --git a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx index c0fb72c08..2a5590caa 100644 --- a/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx +++ b/frontend/src/components/Topics/Topic/Messages/MessagesTable.tsx @@ -5,7 +5,7 @@ import { TopicMessage } from 'generated-sources'; import React, { useState } from 'react'; import { Button } from 'components/common/Button/Button'; import * as S from 'components/common/NewTable/Table.styled'; -import { useGoToNextPage, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; +import { useGoToPrevPage, useGoToNextPage, useIsLiveMode } from 'lib/hooks/useMessagesFilters'; import { useMessageFiltersStore } from 'lib/hooks/useMessageFiltersStore'; import PreviewModal from './PreviewModal'; @@ -21,11 +21,13 @@ const MessagesTable: React.FC = ({ isFetching, }) => { const goToNextPage = useGoToNextPage(); + const goToPrevPage = useGoToPrevPage(); const [previewFor, setPreviewFor] = useState(null); const [keyFilters, setKeyFilters] = useState([]); const [contentFilters, setContentFilters] = useState([]); const nextCursor = useMessageFiltersStore((state) => state.nextCursor); + const prevCursor = useMessageFiltersStore((state) => state.prevCursor); const isLive = useIsLiveMode(); return ( @@ -97,6 +99,14 @@ const MessagesTable: React.FC = ({ +