Skip to content

Commit ef20ab2

Browse files
polegkashtigermanosinHaarolean
authored
ACL: Add BE Search Functionality to ACLs (#636)
Co-authored-by: German Osin <german.osin@gmail.com> Co-authored-by: Roman Zabaluev <gpg@haarolean.dev>
1 parent 099b139 commit ef20ab2

File tree

5 files changed

+48
-7
lines changed

5 files changed

+48
-7
lines changed

api/src/main/java/io/kafbat/ui/controller/AclsController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public Mono<ResponseEntity<Flux<KafkaAclDTO>>> listAcls(String clusterName,
6868
KafkaAclResourceTypeDTO resourceTypeDto,
6969
String resourceName,
7070
KafkaAclNamePatternTypeDTO namePatternTypeDto,
71+
String search,
7172
ServerWebExchange exchange) {
7273
AccessContext context = AccessContext.builder()
7374
.cluster(clusterName)
@@ -88,7 +89,7 @@ public Mono<ResponseEntity<Flux<KafkaAclDTO>>> listAcls(String clusterName,
8889
return validateAccess(context).then(
8990
Mono.just(
9091
ResponseEntity.ok(
91-
aclsService.listAcls(getCluster(clusterName), filter)
92+
aclsService.listAcls(getCluster(clusterName), filter, search)
9293
.map(ClusterMapper::toKafkaAclDto)))
9394
).doOnEach(sig -> audit(context, sig));
9495
}

api/src/main/java/io/kafbat/ui/service/acl/AclsService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ public Mono<Void> deleteAcl(KafkaCluster cluster, AclBinding aclBinding) {
6868
.doOnSuccess(v -> log.info("ACL DELETED: [{}]", aclString));
6969
}
7070

71-
public Flux<AclBinding> listAcls(KafkaCluster cluster, ResourcePatternFilter filter) {
71+
public Flux<AclBinding> listAcls(KafkaCluster cluster, ResourcePatternFilter filter, String principalSearch) {
7272
return adminClientService.get(cluster)
7373
.flatMap(c -> c.listAcls(filter))
7474
.flatMapIterable(acls -> acls)
75+
.filter(acl -> principalSearch == null || acl.entry().principal().contains(principalSearch))
7576
.sort(Comparator.comparing(AclBinding::toString)); //sorting to keep stable order on different calls
7677
}
7778

contract/src/main/resources/swagger/kafbat-ui-api.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,11 @@ paths:
19651965
required: false
19661966
schema:
19671967
$ref: '#/components/schemas/KafkaAclNamePatternType'
1968+
- name: search
1969+
in: query
1970+
required: false
1971+
schema:
1972+
type: string
19681973
responses:
19691974
200:
19701975
description: OK

frontend/src/components/ACLPage/List/List.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React from 'react';
1+
import React, { useEffect, useState } from 'react';
2+
import { useSearchParams } from 'react-router-dom';
23
import { ColumnDef, Row } from '@tanstack/react-table';
34
import Table from 'components/common/NewTable';
45
import { useConfirm } from 'lib/hooks/useConfirm';
@@ -19,14 +20,18 @@ import { useTheme } from 'styled-components';
1920
import ACLFormContext from 'components/ACLPage/Form/AclFormContext';
2021
import PlusIcon from 'components/common/Icons/PlusIcon';
2122
import ActionButton from 'components/common/ActionComponent/ActionButton/ActionButton';
23+
import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel.styled';
24+
import Search from 'components/common/Search/Search';
2225
import ResourcePageHeading from 'components/common/ResourcePageHeading/ResourcePageHeading';
2326
import BreakableTextCell from 'components/common/NewTable/BreakableTextCell';
2427

2528
import * as S from './List.styled';
2629

2730
const ACList: React.FC = () => {
2831
const { clusterName } = useAppParams<{ clusterName: ClusterName }>();
29-
const { data: aclList } = useAcls(clusterName);
32+
const [searchParams, setSearchParams] = useSearchParams();
33+
const [search, setSearch] = useState(searchParams.get('q') || '');
34+
const { data: aclList } = useAcls({ clusterName, search });
3035
const { deleteResource } = useDeleteAcl(clusterName);
3136
const modal = useConfirm(true);
3237
const theme = useTheme();
@@ -37,6 +42,17 @@ const ACList: React.FC = () => {
3742
} = useBoolean();
3843
const [rowId, setRowId] = React.useState('');
3944

45+
useEffect(() => {
46+
const params = new URLSearchParams(searchParams);
47+
if (search) {
48+
params.set('q', search);
49+
params.set('page', '1'); // reset to first page on new search
50+
} else {
51+
params.delete('q');
52+
}
53+
setSearchParams(params, { replace: true });
54+
}, [search]);
55+
4056
const handleDeleteClick = (acl: KafkaAcl | null) => {
4157
if (acl) {
4258
modal('Are you sure want to delete this ACL record?', () =>
@@ -164,6 +180,13 @@ const ACList: React.FC = () => {
164180
<PlusIcon /> Create ACL
165181
</ActionButton>
166182
</ResourcePageHeading>
183+
<ControlPanelWrapper hasInput>
184+
<Search
185+
placeholder="Search by Principal Name"
186+
value={search}
187+
onChange={setSearch}
188+
/>
189+
</ControlPanelWrapper>
167190
<Table
168191
columns={columns}
169192
data={aclList ?? []}

frontend/src/lib/hooks/api/acl.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,22 @@ import {
1414
KafkaAcl,
1515
} from 'generated-sources';
1616

17-
export function useAcls(clusterName: ClusterName) {
17+
export function useAcls({
18+
clusterName,
19+
search,
20+
}: {
21+
clusterName: ClusterName;
22+
search?: string;
23+
}) {
1824
return useQuery(
19-
['clusters', clusterName, 'acls'],
20-
() => api.listAcls({ clusterName }),
25+
['clusters', clusterName, 'acls', { search }],
26+
() =>
27+
api.listAcls({
28+
clusterName,
29+
search,
30+
}),
2131
{
32+
keepPreviousData: true,
2233
suspense: false,
2334
}
2435
);

0 commit comments

Comments
 (0)