From a2f591013fa41f39c728efaf16ac0758625de840 Mon Sep 17 00:00:00 2001 From: Poleg Kashti Date: Sat, 26 Oct 2024 19:32:53 +0300 Subject: [PATCH 1/4] Closes #621 --- .../io/kafbat/ui/controller/AclsController.java | 3 ++- .../io/kafbat/ui/service/acl/AclsService.java | 3 ++- .../main/resources/swagger/kafbat-ui-api.yaml | 5 +++++ frontend/src/components/ACLPage/List/List.tsx | 17 +++++++++++++++-- frontend/src/lib/hooks/api/acl.ts | 15 +++++++++++---- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/io/kafbat/ui/controller/AclsController.java b/api/src/main/java/io/kafbat/ui/controller/AclsController.java index bbb30b5c6..b99c8106d 100644 --- a/api/src/main/java/io/kafbat/ui/controller/AclsController.java +++ b/api/src/main/java/io/kafbat/ui/controller/AclsController.java @@ -67,6 +67,7 @@ public Mono>> listAcls(String clusterName, KafkaAclResourceTypeDTO resourceTypeDto, String resourceName, KafkaAclNamePatternTypeDTO namePatternTypeDto, + String search, ServerWebExchange exchange) { AccessContext context = AccessContext.builder() .cluster(clusterName) @@ -87,7 +88,7 @@ public Mono>> listAcls(String clusterName, return validateAccess(context).then( Mono.just( ResponseEntity.ok( - aclsService.listAcls(getCluster(clusterName), filter) + aclsService.listAcls(getCluster(clusterName), filter, search) .map(ClusterMapper::toKafkaAclDto))) ).doOnEach(sig -> audit(context, sig)); } diff --git a/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java b/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java index 30078d435..c0ee2469a 100644 --- a/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java +++ b/api/src/main/java/io/kafbat/ui/service/acl/AclsService.java @@ -68,10 +68,11 @@ public Mono deleteAcl(KafkaCluster cluster, AclBinding aclBinding) { .doOnSuccess(v -> log.info("ACL DELETED: [{}]", aclString)); } - public Flux listAcls(KafkaCluster cluster, ResourcePatternFilter filter) { + public Flux listAcls(KafkaCluster cluster, ResourcePatternFilter filter, String principalSearch) { return adminClientService.get(cluster) .flatMap(c -> c.listAcls(filter)) .flatMapIterable(acls -> acls) + .filter(acl -> principalSearch == null || acl.entry().principal().contains(principalSearch)) .sort(Comparator.comparing(AclBinding::toString)); //sorting to keep stable order on different calls } diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 454d78c7c..415759dc2 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -1965,6 +1965,11 @@ paths: required: false schema: $ref: '#/components/schemas/KafkaAclNamePatternType' + - name: search + in: query + required: false + schema: + type: string responses: 200: description: OK diff --git a/frontend/src/components/ACLPage/List/List.tsx b/frontend/src/components/ACLPage/List/List.tsx index 26155172b..e3c1d3501 100644 --- a/frontend/src/components/ACLPage/List/List.tsx +++ b/frontend/src/components/ACLPage/List/List.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { ColumnDef, Row } from '@tanstack/react-table'; import PageHeading from 'components/common/PageHeading/PageHeading'; import Table from 'components/common/NewTable'; @@ -20,12 +21,16 @@ import { useTheme } from 'styled-components'; import ACLFormContext from 'components/ACLPage/Form/AclFormContext'; import PlusIcon from 'components/common/Icons/PlusIcon'; import ActionButton from 'components/common/ActionComponent/ActionButton/ActionButton'; +import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel.styled'; +import Search from 'components/common/Search/Search'; import * as S from './List.styled'; const ACList: React.FC = () => { const { clusterName } = useAppParams<{ clusterName: ClusterName }>(); - const { data: aclList } = useAcls(clusterName); + const [searchParams, setSearchParams] = useSearchParams(); + const [search, setSearch] = useState(searchParams.get('q') || ''); + const { data: aclList } = useAcls({ clusterName, search }); const { deleteResource } = useDeleteAcl(clusterName); const modal = useConfirm(true); const theme = useTheme(); @@ -36,6 +41,11 @@ const ACList: React.FC = () => { } = useBoolean(); const [rowId, setRowId] = React.useState(''); + // Set the search params to the url based on the localStorage value + useEffect(() => { + setSearch(searchParams.get('q') || ''); + }, [searchParams]); + const handleDeleteClick = (acl: KafkaAcl | null) => { if (acl) { modal('Are you sure want to delete this ACL record?', () => @@ -162,6 +172,9 @@ const ACList: React.FC = () => { Create ACL + + + api.listAcls({ clusterName }), + ['clusters', clusterName, 'acls', { search }], + () => api.listAcls({ + clusterName, + search + }), { - suspense: false, + keepPreviousData: true, + suspense: false, } ); } From 7e37975de64dcfc9eafaf176a1b551839e882986 Mon Sep 17 00:00:00 2001 From: Poleg Kashti Date: Sat, 26 Oct 2024 20:13:23 +0300 Subject: [PATCH 2/4] Fix(List.tsx): remove unused variable --- frontend/src/components/ACLPage/List/List.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/ACLPage/List/List.tsx b/frontend/src/components/ACLPage/List/List.tsx index e3c1d3501..9808a6c1c 100644 --- a/frontend/src/components/ACLPage/List/List.tsx +++ b/frontend/src/components/ACLPage/List/List.tsx @@ -28,7 +28,7 @@ import * as S from './List.styled'; const ACList: React.FC = () => { const { clusterName } = useAppParams<{ clusterName: ClusterName }>(); - const [searchParams, setSearchParams] = useSearchParams(); + const [searchParams] = useSearchParams(); const [search, setSearch] = useState(searchParams.get('q') || ''); const { data: aclList } = useAcls({ clusterName, search }); const { deleteResource } = useDeleteAcl(clusterName); From e46da2d16048596201516824b0de752209b4a17d Mon Sep 17 00:00:00 2001 From: Poleg Kashti Date: Sat, 26 Oct 2024 20:59:58 +0300 Subject: [PATCH 3/4] Fix(acl.ts): lint warnings --- frontend/src/lib/hooks/api/acl.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/hooks/api/acl.ts b/frontend/src/lib/hooks/api/acl.ts index 7e69f2eb4..a1c1b1ad1 100644 --- a/frontend/src/lib/hooks/api/acl.ts +++ b/frontend/src/lib/hooks/api/acl.ts @@ -14,19 +14,23 @@ import { KafkaAcl, } from 'generated-sources'; -export function useAcls({ clusterName, search }: { - clusterName: ClusterName; - search?: string; +export function useAcls({ + clusterName, + search, +}: { + clusterName: ClusterName; + search?: string; }) { return useQuery( ['clusters', clusterName, 'acls', { search }], - () => api.listAcls({ + () => + api.listAcls({ clusterName, - search - }), + search, + }), { - keepPreviousData: true, - suspense: false, + keepPreviousData: true, + suspense: false, } ); } From 7ca2470fa9732279310ed6e8e75f72ee70af7181 Mon Sep 17 00:00:00 2001 From: Poleg Kashti Date: Fri, 9 May 2025 03:47:33 +0300 Subject: [PATCH 4/4] fix(acls): sync `q` param in URL and fix back/forward history --- frontend/src/components/ACLPage/List/List.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/ACLPage/List/List.tsx b/frontend/src/components/ACLPage/List/List.tsx index 9808a6c1c..41ac2ca64 100644 --- a/frontend/src/components/ACLPage/List/List.tsx +++ b/frontend/src/components/ACLPage/List/List.tsx @@ -28,7 +28,7 @@ import * as S from './List.styled'; const ACList: React.FC = () => { const { clusterName } = useAppParams<{ clusterName: ClusterName }>(); - const [searchParams] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams(); const [search, setSearch] = useState(searchParams.get('q') || ''); const { data: aclList } = useAcls({ clusterName, search }); const { deleteResource } = useDeleteAcl(clusterName); @@ -41,10 +41,16 @@ const ACList: React.FC = () => { } = useBoolean(); const [rowId, setRowId] = React.useState(''); - // Set the search params to the url based on the localStorage value useEffect(() => { - setSearch(searchParams.get('q') || ''); - }, [searchParams]); + const params = new URLSearchParams(searchParams); + if (search) { + params.set('q', search); + params.set('page', '1'); // reset to first page on new search + } else { + params.delete('q'); + } + setSearchParams(params, { replace: true }); + }, [search]); const handleDeleteClick = (acl: KafkaAcl | null) => { if (acl) { @@ -173,7 +179,11 @@ const ACList: React.FC = () => { - +