diff --git a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java index 5d5128fea..46a178f62 100644 --- a/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java +++ b/api/src/main/java/io/kafbat/ui/config/ClustersProperties.java @@ -243,11 +243,23 @@ public static class NgramProperties { @NoArgsConstructor @AllArgsConstructor public static class ClusterFtsProperties { - boolean enabled = false; + boolean enabled = true; + boolean defaultEnabled = false; NgramProperties schemas = new NgramProperties(1, 4); NgramProperties consumers = new NgramProperties(1, 4); NgramProperties connect = new NgramProperties(1, 4); NgramProperties acl = new NgramProperties(1, 4); + + public boolean use(Boolean request) { + if (enabled) { + if (Boolean.TRUE.equals(request)) { + return true; + } else if (request == null && defaultEnabled) { + return true; + } + } + return false; + } } @PostConstruct 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 1722a97e7..190b1081c 100644 --- a/api/src/main/java/io/kafbat/ui/controller/AclsController.java +++ b/api/src/main/java/io/kafbat/ui/controller/AclsController.java @@ -25,7 +25,7 @@ @RestController @RequiredArgsConstructor -public class AclsController extends AbstractController implements AclsApi, McpTool { +public class AclsController extends AbstractController implements AclsApi, McpTool { private final AclsService aclsService; @@ -69,6 +69,7 @@ public Mono>> listAcls(String clusterName, String resourceName, KafkaAclNamePatternTypeDTO namePatternTypeDto, String search, + Boolean fts, ServerWebExchange exchange) { AccessContext context = AccessContext.builder() .cluster(clusterName) @@ -89,7 +90,7 @@ public Mono>> listAcls(String clusterName, return validateAccess(context).then( Mono.just( ResponseEntity.ok( - aclsService.listAcls(getCluster(clusterName), filter, search) + aclsService.listAcls(getCluster(clusterName), filter, search, fts) .map(ClusterMapper::toKafkaAclDto))) ).doOnEach(sig -> audit(context, sig)); } diff --git a/api/src/main/java/io/kafbat/ui/controller/ConsumerGroupsController.java b/api/src/main/java/io/kafbat/ui/controller/ConsumerGroupsController.java index 916ed185f..0b3a7d69a 100644 --- a/api/src/main/java/io/kafbat/ui/controller/ConsumerGroupsController.java +++ b/api/src/main/java/io/kafbat/ui/controller/ConsumerGroupsController.java @@ -128,6 +128,7 @@ public Mono> getConsumerGroupsPage String search, ConsumerGroupOrderingDTO orderBy, SortOrderDTO sortOrderDto, + Boolean fts, ServerWebExchange exchange) { var context = AccessContext.builder() diff --git a/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java b/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java index 2072f0e4c..e535d0ea6 100644 --- a/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java +++ b/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java @@ -129,6 +129,7 @@ public Mono>> getAllConnectors( String search, ConnectorColumnsToSortDTO orderBy, SortOrderDTO sortOrder, + Boolean fts, ServerWebExchange exchange ) { var context = AccessContext.builder() @@ -140,7 +141,7 @@ public Mono>> getAllConnectors( ? getConnectorsComparator(orderBy) : getConnectorsComparator(orderBy).reversed(); - Flux job = kafkaConnectService.getAllConnectors(getCluster(clusterName), search) + Flux job = kafkaConnectService.getAllConnectors(getCluster(clusterName), search, fts) .filterWhen(dto -> accessControlService.isConnectAccessible(dto.getConnect(), clusterName)) .sort(comparator); diff --git a/api/src/main/java/io/kafbat/ui/controller/SchemasController.java b/api/src/main/java/io/kafbat/ui/controller/SchemasController.java index 3421a1563..26c06fb21 100644 --- a/api/src/main/java/io/kafbat/ui/controller/SchemasController.java +++ b/api/src/main/java/io/kafbat/ui/controller/SchemasController.java @@ -217,13 +217,15 @@ public Mono> getSchemas(String cluster @Valid String search, SchemaColumnsToSortDTO orderBy, SortOrderDTO sortOrder, + Boolean fts, ServerWebExchange serverWebExchange) { var context = AccessContext.builder() .cluster(clusterName) .operationName("getSchemas") .build(); - ClustersProperties.ClusterFtsProperties fts = clustersProperties.getFts(); + ClustersProperties.ClusterFtsProperties ftsProperties = clustersProperties.getFts(); + boolean useFts = ftsProperties.use(fts); return schemaRegistryService .getAllSubjectNames(getCluster(clusterName)) @@ -234,7 +236,7 @@ public Mono> getSchemas(String cluster int pageSize = perPage != null && perPage > 0 ? perPage : DEFAULT_PAGE_SIZE; int subjectToSkip = ((pageNum != null && pageNum > 0 ? pageNum : 1) - 1) * pageSize; - SchemasFilter filter = new SchemasFilter(subjects, fts.isEnabled(), fts.getSchemas()); + SchemasFilter filter = new SchemasFilter(subjects, useFts, ftsProperties.getSchemas()); List filteredSubjects = new ArrayList<>(filter.find(search)); var totalPages = (filteredSubjects.size() / pageSize) diff --git a/api/src/main/java/io/kafbat/ui/controller/TopicsController.java b/api/src/main/java/io/kafbat/ui/controller/TopicsController.java index 0372da2bd..a5036011a 100644 --- a/api/src/main/java/io/kafbat/ui/controller/TopicsController.java +++ b/api/src/main/java/io/kafbat/ui/controller/TopicsController.java @@ -174,6 +174,7 @@ public Mono> getTopics(String clusterName, @Valid String search, @Valid TopicColumnsToSortDTO orderBy, @Valid SortOrderDTO sortOrder, + Boolean fts, ServerWebExchange exchange) { AccessContext context = AccessContext.builder() @@ -181,13 +182,14 @@ public Mono> getTopics(String clusterName, .operationName("getTopics") .build(); - return topicsService.getTopicsForPagination(getCluster(clusterName), search, showInternal) + return topicsService.getTopicsForPagination(getCluster(clusterName), search, showInternal, fts) .flatMap(topics -> accessControlService.filterViewableTopics(topics, clusterName)) .flatMap(topics -> { int pageSize = perPage != null && perPage > 0 ? perPage : DEFAULT_PAGE_SIZE; var topicsToSkip = ((page != null && page > 0 ? page : 1) - 1) * pageSize; - ClustersProperties.ClusterFtsProperties fts = clustersProperties.getFts(); - Comparator comparatorForTopic = getComparatorForTopic(orderBy, fts.isEnabled()); + ClustersProperties.ClusterFtsProperties ftsProperties = clustersProperties.getFts(); + boolean useFts = ftsProperties.use(fts); + Comparator comparatorForTopic = getComparatorForTopic(orderBy, useFts); var comparator = sortOrder == null || !sortOrder.equals(SortOrderDTO.DESC) ? comparatorForTopic : comparatorForTopic.reversed(); diff --git a/api/src/main/java/io/kafbat/ui/model/ClusterFeature.java b/api/src/main/java/io/kafbat/ui/model/ClusterFeature.java index bdd66ea04..79d29bcdb 100644 --- a/api/src/main/java/io/kafbat/ui/model/ClusterFeature.java +++ b/api/src/main/java/io/kafbat/ui/model/ClusterFeature.java @@ -8,5 +8,7 @@ public enum ClusterFeature { KAFKA_ACL_VIEW, KAFKA_ACL_EDIT, CLIENT_QUOTA_MANAGEMENT, - GRAPHS_ENABLED + GRAPHS_ENABLED, + FTS_ENABLED, + FTS_DEFAULT_ENABLED } diff --git a/api/src/main/java/io/kafbat/ui/service/FeatureService.java b/api/src/main/java/io/kafbat/ui/service/FeatureService.java index 8fb19d065..bb203bb3a 100644 --- a/api/src/main/java/io/kafbat/ui/service/FeatureService.java +++ b/api/src/main/java/io/kafbat/ui/service/FeatureService.java @@ -2,6 +2,7 @@ import static io.kafbat.ui.service.ReactiveAdminClient.SupportedFeature.CLIENT_QUOTA_MANAGEMENT; +import io.kafbat.ui.config.ClustersProperties; import io.kafbat.ui.model.ClusterFeature; import io.kafbat.ui.model.KafkaCluster; import io.kafbat.ui.service.ReactiveAdminClient.ClusterDescription; @@ -11,6 +12,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Predicate; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.common.acl.AclOperation; import org.springframework.stereotype.Service; @@ -19,7 +21,9 @@ @Service @Slf4j +@RequiredArgsConstructor public class FeatureService { + private final ClustersProperties clustersProperties; public Mono> getAvailableFeatures(ReactiveAdminClient adminClient, KafkaCluster cluster, @@ -49,6 +53,16 @@ public Mono> getAvailableFeatures(ReactiveAdminClient admin features.add(aclEdit(adminClient, clusterDescription)); features.add(quotaManagement(adminClient)); + if (clustersProperties.getFts() != null) { + ClustersProperties.ClusterFtsProperties fts = clustersProperties.getFts(); + if (fts.isEnabled()) { + features.add(Mono.just(ClusterFeature.FTS_ENABLED)); + if (fts.isDefaultEnabled()) { + features.add(Mono.just(ClusterFeature.FTS_DEFAULT_ENABLED)); + } + } + } + return Flux.fromIterable(features).flatMap(m -> m).collectList(); } diff --git a/api/src/main/java/io/kafbat/ui/service/KafkaConnectService.java b/api/src/main/java/io/kafbat/ui/service/KafkaConnectService.java index 2d6dfae15..f7d6948d1 100644 --- a/api/src/main/java/io/kafbat/ui/service/KafkaConnectService.java +++ b/api/src/main/java/io/kafbat/ui/service/KafkaConnectService.java @@ -1,7 +1,5 @@ package io.kafbat.ui.service; -import static org.apache.commons.lang3.Strings.CI; - import com.github.benmanes.caffeine.cache.AsyncCache; import com.github.benmanes.caffeine.cache.Caffeine; import io.kafbat.ui.config.ClustersProperties; @@ -134,7 +132,7 @@ private Flux getConnectConnectors( } public Flux getAllConnectors(final KafkaCluster cluster, - @Nullable final String search) { + @Nullable final String search, Boolean fts) { return getConnects(cluster, false) .flatMap(connect -> getConnectorNamesWithErrorsSuppress(cluster, connect.getName()) @@ -153,14 +151,17 @@ public Flux getAllConnectors(final KafkaCluster cluster, .build()))) .map(kafkaConnectMapper::fullConnectorInfo) .collectList() - .map(lst -> filterConnectors(lst, search)) + .map(lst -> filterConnectors(lst, search, fts)) .flatMapMany(Flux::fromIterable); } - private List filterConnectors(List connectors, String search) { - ClustersProperties.ClusterFtsProperties fts = clustersProperties.getFts(); + private List filterConnectors( + List connectors, + String search, + Boolean fts) { + boolean useFts = clustersProperties.getFts().use(fts); KafkaConnectNgramFilter filter = - new KafkaConnectNgramFilter(connectors, fts.isEnabled(), fts.getConnect()); + new KafkaConnectNgramFilter(connectors, useFts, clustersProperties.getFts().getConnect()); return filter.find(search); } diff --git a/api/src/main/java/io/kafbat/ui/service/TopicsService.java b/api/src/main/java/io/kafbat/ui/service/TopicsService.java index 79da561dd..8f15d01e8 100644 --- a/api/src/main/java/io/kafbat/ui/service/TopicsService.java +++ b/api/src/main/java/io/kafbat/ui/service/TopicsService.java @@ -26,7 +26,6 @@ import io.kafbat.ui.model.TopicUpdateDTO; import io.kafbat.ui.service.metrics.scrape.ScrapedClusterState; import io.kafbat.ui.service.metrics.scrape.ScrapedClusterState.TopicState; -import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; @@ -49,7 +48,6 @@ import org.apache.kafka.common.errors.TopicExistsException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.retry.Retry; @@ -467,13 +465,14 @@ public Mono cloneTopic( ); } - public Mono> getTopicsForPagination(KafkaCluster cluster, String search, Boolean showInternal) { + public Mono> getTopicsForPagination(KafkaCluster cluster, String search, Boolean showInternal, + Boolean fts) { Statistics stats = statisticsCache.get(cluster); ScrapedClusterState clusterState = stats.getClusterState(); - + boolean useFts = clustersProperties.getFts().use(fts); try { return Mono.just( - clusterState.getTopicIndex().find(search, showInternal, null) + clusterState.getTopicIndex().find(search, showInternal, useFts, null) ).flatMap(lst -> filterExisting(cluster, lst)).map(lst -> lst.stream().map(t -> t.withMetrics(stats.getMetrics())).toList() ); 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 9e9943946..301c84277 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 @@ -71,20 +71,21 @@ public Mono deleteAcl(KafkaCluster cluster, AclBinding aclBinding) { .doOnSuccess(v -> log.info("ACL DELETED: [{}]", aclString)); } - public Flux listAcls(KafkaCluster cluster, ResourcePatternFilter filter, String principalSearch) { + public Flux listAcls(KafkaCluster cluster, ResourcePatternFilter filter, String principalSearch, + Boolean fts) { return adminClientService.get(cluster) .flatMap(c -> c.listAcls(filter)) .flatMapIterable(acls -> acls) .filter(acl -> principalSearch == null || acl.entry().principal().contains(principalSearch)) .collectList() - .map(lst -> filter(lst, principalSearch)) + .map(lst -> filter(lst, principalSearch, fts)) .flatMapMany(Flux::fromIterable) .sort(Comparator.comparing(AclBinding::toString)); //sorting to keep stable order on different calls } - private List filter(List acls, String principalSearch) { - ClustersProperties.ClusterFtsProperties fts = clustersProperties.getFts(); - AclBindingNgramFilter filter = new AclBindingNgramFilter(acls, fts.isEnabled(), fts.getAcl()); + private List filter(List acls, String principalSearch, Boolean fts) { + boolean useFts = clustersProperties.getFts().use(fts); + AclBindingNgramFilter filter = new AclBindingNgramFilter(acls, useFts, clustersProperties.getFts().getAcl()); return filter.find(principalSearch); } diff --git a/api/src/main/java/io/kafbat/ui/service/index/FilterTopicIndex.java b/api/src/main/java/io/kafbat/ui/service/index/FilterTopicIndex.java index 0b5a30900..137281279 100644 --- a/api/src/main/java/io/kafbat/ui/service/index/FilterTopicIndex.java +++ b/api/src/main/java/io/kafbat/ui/service/index/FilterTopicIndex.java @@ -4,18 +4,20 @@ import io.kafbat.ui.model.InternalTopic; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.stream.Stream; public class FilterTopicIndex implements TopicsIndex { - private List topics; + private Collection topics; - public FilterTopicIndex(List topics) { + public FilterTopicIndex(Collection topics) { this.topics = topics; } @Override - public List find(String search, Boolean showInternal, String sort, Integer count) { + public List find(String search, Boolean showInternal, String sort, + boolean fts, Integer count) { if (search == null || search.isBlank()) { return new ArrayList<>(this.topics); } diff --git a/api/src/main/java/io/kafbat/ui/service/index/LuceneTopicsIndex.java b/api/src/main/java/io/kafbat/ui/service/index/LuceneTopicsIndex.java index 6953add26..9d9c8bf5a 100644 --- a/api/src/main/java/io/kafbat/ui/service/index/LuceneTopicsIndex.java +++ b/api/src/main/java/io/kafbat/ui/service/index/LuceneTopicsIndex.java @@ -1,6 +1,5 @@ package io.kafbat.ui.service.index; -import io.kafbat.ui.config.ClustersProperties; import io.kafbat.ui.model.InternalTopic; import io.kafbat.ui.model.InternalTopicConfig; import java.io.IOException; @@ -98,7 +97,15 @@ public void close() throws Exception { } } - public List find(String search, Boolean showInternal, String sort, Integer count) { + public List find(String search, Boolean showInternal, String sort, + boolean fts, Integer count) { + if (!fts) { + try (FilterTopicIndex filter = new FilterTopicIndex(this.topicMap.values())) { + return filter.find(search, showInternal, sort, fts, count); + } catch (Exception e) { + throw new RuntimeException(e); + } + } return find(search, showInternal, sort, count, 0.0f); } diff --git a/api/src/main/java/io/kafbat/ui/service/index/TopicsIndex.java b/api/src/main/java/io/kafbat/ui/service/index/TopicsIndex.java index 14fc36501..4b498f940 100644 --- a/api/src/main/java/io/kafbat/ui/service/index/TopicsIndex.java +++ b/api/src/main/java/io/kafbat/ui/service/index/TopicsIndex.java @@ -28,9 +28,9 @@ enum FieldType { FIELD_CONFIG_PREFIX, FieldType.STRING ); - default List find(String search, Boolean showInternal, Integer count) { - return this.find(search, showInternal, FIELD_NAME, count); + default List find(String search, Boolean showInternal, boolean fts, Integer count) { + return this.find(search, showInternal, FIELD_NAME, fts, count); } - List find(String search, Boolean showInternal, String sort, Integer count); + List find(String search, Boolean showInternal, String sort, boolean fts, Integer count); } diff --git a/api/src/test/java/io/kafbat/ui/service/SchemaRegistryPaginationTest.java b/api/src/test/java/io/kafbat/ui/service/SchemaRegistryPaginationTest.java index deab7ffd5..5152c7f5d 100644 --- a/api/src/test/java/io/kafbat/ui/service/SchemaRegistryPaginationTest.java +++ b/api/src/test/java/io/kafbat/ui/service/SchemaRegistryPaginationTest.java @@ -79,7 +79,7 @@ void shouldListFirst25andThen10Schemas() { .toList() ); var schemasFirst25 = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, - null, null, null, null, null, null).block(); + null, null, null, null, null, null, null).block(); assertThat(schemasFirst25).isNotNull(); assertThat(schemasFirst25.getBody()).isNotNull(); assertThat(schemasFirst25.getBody().getPageCount()).isEqualTo(4); @@ -88,7 +88,7 @@ void shouldListFirst25andThen10Schemas() { .isSortedAccordingTo(Comparator.comparing(SchemaSubjectDTO::getSubject)); var schemasFirst10 = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, - null, 10, null, null, null, null).block(); + null, 10, null, null, null, null, null).block(); assertThat(schemasFirst10).isNotNull(); assertThat(schemasFirst10.getBody()).isNotNull(); @@ -107,7 +107,7 @@ void shouldListSchemasContaining_1() { .toList() ); var schemasSearch7 = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, - null, null, "1", null, null, null).block(); + null, null, "1", null, null, null, null).block(); assertThat(schemasSearch7).isNotNull(); assertThat(schemasSearch7.getBody()).isNotNull(); assertThat(schemasSearch7.getBody().getPageCount()).isEqualTo(1); @@ -123,7 +123,7 @@ void shouldCorrectlyHandleNonPositivePageNumberAndPageSize() { .toList() ); var schemas = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, - 0, -1, null, null, null, null).block(); + 0, -1, null, null, null, null, null).block(); assertThat(schemas).isNotNull(); assertThat(schemas.getBody()).isNotNull(); @@ -142,7 +142,7 @@ void shouldCalculateCorrectPageCountForNonDivisiblePageSize() { ); var schemas = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, - 4, 33, null, null, null, null).block(); + 4, 33, null, null, null, null, null).block(); assertThat(schemas).isNotNull(); assertThat(schemas.getBody()).isNotNull(); @@ -177,7 +177,7 @@ void shouldOrderByAndPaginate() { var schemasFirst25 = controller.getSchemas(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, - SchemaColumnsToSortDTO.ID, SortOrderDTO.DESC, null + SchemaColumnsToSortDTO.ID, SortOrderDTO.DESC, null, null ).block(); List last25OrderedById = schemas.stream() diff --git a/api/src/test/java/io/kafbat/ui/service/TopicsServicePaginationTest.java b/api/src/test/java/io/kafbat/ui/service/TopicsServicePaginationTest.java index ff3a621ed..ec0a77264 100644 --- a/api/src/test/java/io/kafbat/ui/service/TopicsServicePaginationTest.java +++ b/api/src/test/java/io/kafbat/ui/service/TopicsServicePaginationTest.java @@ -103,13 +103,13 @@ private void init(Map topicsInCache) { when(reactiveAdminClient.listTopics(anyBoolean())).thenReturn(Mono.just(topicsInCache.keySet())); when(clustersStorage.getClusterByName(isA(String.class))) .thenReturn(Optional.of(kafkaCluster)); - when(mockTopicsService.getTopicsForPagination(isA(KafkaCluster.class), any(), any())) + when(mockTopicsService.getTopicsForPagination(isA(KafkaCluster.class), any(), any(), any())) .thenAnswer(a -> topicsService.getTopicsForPagination( a.getArgument(0), a.getArgument(1), - a.getArgument(2) - ) + a.getArgument(2), + a.getArgument(3)) ); @@ -151,8 +151,8 @@ void shouldListFirst25Topics() { ); var topics = topicsController - .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, null, - null, null, null).block(); + .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, null, + null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(25); @@ -178,7 +178,7 @@ void shouldListFirst25TopicsSortedByNameDescendingOrder() { var topics = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, null, - TopicColumnsToSortDTO.NAME, SortOrderDTO.DESC, null).block(); + TopicColumnsToSortDTO.NAME, SortOrderDTO.DESC, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(25); @@ -204,7 +204,7 @@ void shouldCalculateCorrectPageCountForNonDivisiblePageSize() { ); var topics = topicsController - .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 4, 33, null, null, null, null, null).block(); + .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 4, 33, null, null, null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(1); @@ -223,7 +223,7 @@ void shouldCorrectlyHandleNonPositivePageNumberAndPageSize() { ); var topics = topicsController - .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 0, -1, null, null, null, null, null).block(); + .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 0, -1, null, null, null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(25); @@ -243,7 +243,7 @@ void shouldListBotInternalAndNonInternalTopics() { var topics = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 0, -1, true, null, - null, null, null).block(); + null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(25); @@ -264,7 +264,7 @@ void shouldListOnlyNonInternalTopics() { var topics = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, 4, -1, false, null, - null, null, null).block(); + null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(4); assertThat(topics.getBody().getTopics()).hasSize(5); @@ -285,7 +285,7 @@ void shouldListOnlyTopicsContainingOne() { var topics = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, "1", - null, null, null).block(); + null, null, null, null).block(); assertThat(topics.getBody().getPageCount()).isEqualTo(1); assertThat(topics.getBody().getTopics()).hasSize(20); @@ -308,7 +308,7 @@ void shouldListTopicsOrderedByPartitionsCount() { var topicsSortedAsc = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, - null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, null, null).block(); + null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, null, null, null).block(); assertThat(topicsSortedAsc.getBody().getPageCount()).isEqualTo(4); assertThat(topicsSortedAsc.getBody().getTopics()).hasSize(25); @@ -322,7 +322,7 @@ void shouldListTopicsOrderedByPartitionsCount() { var topicsSortedDesc = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, - null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, SortOrderDTO.DESC, null).block(); + null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, SortOrderDTO.DESC, null, null).block(); assertThat(topicsSortedDesc.getBody().getPageCount()).isEqualTo(4); assertThat(topicsSortedDesc.getBody().getTopics()).hasSize(25); @@ -363,7 +363,7 @@ void shouldListTopicsOrderedByMessagesCount() { var topicsSortedAsc = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, - null, TopicColumnsToSortDTO.MESSAGES_COUNT, null, null).block(); + null, TopicColumnsToSortDTO.MESSAGES_COUNT, null, null, null).block(); assertThat(topicsSortedAsc.getBody().getPageCount()).isEqualTo(4); assertThat(topicsSortedAsc.getBody().getTopics()).hasSize(25); @@ -379,7 +379,7 @@ void shouldListTopicsOrderedByMessagesCount() { var topicsSortedDesc = topicsController .getTopics(LOCAL_KAFKA_CLUSTER_NAME, null, null, null, - null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, SortOrderDTO.DESC, null).block(); + null, TopicColumnsToSortDTO.TOTAL_PARTITIONS, SortOrderDTO.DESC, null, null).block(); assertThat(topicsSortedDesc.getBody().getPageCount()).isEqualTo(4); assertThat(topicsSortedDesc.getBody().getTopics()).hasSize(25); diff --git a/api/src/test/java/io/kafbat/ui/service/index/LuceneTopicsIndexTest.java b/api/src/test/java/io/kafbat/ui/service/index/LuceneTopicsIndexTest.java index 557c2a785..eafa5889b 100644 --- a/api/src/test/java/io/kafbat/ui/service/index/LuceneTopicsIndexTest.java +++ b/api/src/test/java/io/kafbat/ui/service/index/LuceneTopicsIndexTest.java @@ -1,11 +1,9 @@ package io.kafbat.ui.service.index; -import io.kafbat.ui.config.ClustersProperties; import io.kafbat.ui.model.InternalPartition; import io.kafbat.ui.model.InternalTopic; import io.kafbat.ui.model.InternalTopicConfig; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -75,7 +73,7 @@ void testFindTopicsByName() throws Exception { SoftAssertions softly = new SoftAssertions(); try (LuceneTopicsIndex index = new LuceneTopicsIndex(topics)) { for (Map.Entry entry : examples.entrySet()) { - List resultAll = index.find(entry.getKey(), null, topics.size()); + List resultAll = index.find(entry.getKey(), null, true, topics.size()); softly.assertThat(resultAll.size()) .withFailMessage("Expected %d results for '%s', but got %s", entry.getValue(), entry.getKey(), resultAll) .isEqualTo(entry.getValue()); diff --git a/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java b/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java index eaa1d9cbc..db10fb1b0 100644 --- a/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java +++ b/api/src/test/java/io/kafbat/ui/service/mcp/McpSpecificationGeneratorTest.java @@ -79,7 +79,8 @@ void testConvertController() { "showInternal", Map.of("type", "boolean"), "search", Map.of("type", "string"), "orderBy", SCHEMA_GENERATOR.generateSchema(TopicColumnsToSortDTO.class), - "sortOrder", SCHEMA_GENERATOR.generateSchema(SortOrderDTO.class) + "sortOrder", SCHEMA_GENERATOR.generateSchema(SortOrderDTO.class), + "fts", Map.of("type", "boolean") ), List.of("clusterName"), false, null, null) ), new McpSchema.Tool( diff --git a/contract-typespec/api/acls.tsp b/contract-typespec/api/acls.tsp index a56ddea5c..e3b8a41e8 100644 --- a/contract-typespec/api/acls.tsp +++ b/contract-typespec/api/acls.tsp @@ -16,7 +16,8 @@ interface AclApi { @query resourceType?: KafkaAclResourceType, @query resourceName?: string, @query namePatternType?: KafkaAclNamePatternType, - @query search?: string + @query search?: string, + @query fts?: boolean ): KafkaAcl[]; @route("/csv") diff --git a/contract-typespec/api/clusters.tsp b/contract-typespec/api/clusters.tsp index 388aeae8c..9b7f828f2 100644 --- a/contract-typespec/api/clusters.tsp +++ b/contract-typespec/api/clusters.tsp @@ -56,7 +56,9 @@ alias ClusterFeature = | "KAFKA_ACL_VIEW" | "KAFKA_ACL_EDIT" | "CLIENT_QUOTA_MANAGEMENT" - | "GRAPHS_ENABLED"; + | "GRAPHS_ENABLED" + | "FTS_ENABLED" + | "FTS_DEFAULT_ENABLED"; enum ServerStatus { ONLINE, diff --git a/contract-typespec/api/consumer-groups.tsp b/contract-typespec/api/consumer-groups.tsp index d901fc001..8e13d588e 100644 --- a/contract-typespec/api/consumer-groups.tsp +++ b/contract-typespec/api/consumer-groups.tsp @@ -20,6 +20,7 @@ interface ConsumerGroupsApi { @query search?: string, @query orderBy?: ConsumerGroupOrdering, @query sortOrder?: SortOrder, + @query fts?: boolean ): ConsumerGroupsPageResponse; @get diff --git a/contract-typespec/api/kafka-connect.tsp b/contract-typespec/api/kafka-connect.tsp index 585e0ac79..697e4bfd4 100644 --- a/contract-typespec/api/kafka-connect.tsp +++ b/contract-typespec/api/kafka-connect.tsp @@ -47,6 +47,7 @@ interface ConnectorsApi { @query search?: string, @query orderBy?: ConnectorColumnsToSort, @query sortOrder?: SortOrder, + @query fts?: boolean ): FullConnectorInfo[]; } diff --git a/contract-typespec/api/schemas.tsp b/contract-typespec/api/schemas.tsp index 98ba93905..89170877c 100644 --- a/contract-typespec/api/schemas.tsp +++ b/contract-typespec/api/schemas.tsp @@ -29,6 +29,7 @@ interface SchemasApi { @query search?: string, @query orderBy?: SchemaColumnsToSort, @query sortOrder?: SortOrder, + @query fts?: boolean ): SchemaSubjectsResponse; @delete diff --git a/contract-typespec/api/topics.tsp b/contract-typespec/api/topics.tsp index 5fad04813..7ae8a9682 100644 --- a/contract-typespec/api/topics.tsp +++ b/contract-typespec/api/topics.tsp @@ -21,6 +21,7 @@ interface TopicsApi { @query search?: string, @query orderBy?: TopicColumnsToSort, @query sortOrder?: SortOrder, + @query fts?: boolean ): TopicsResponse; @post diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 4ea659928..a50a755a2 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -378,6 +378,11 @@ paths: required: false schema: $ref: '#/components/schemas/SortOrder' + - name: fts + in: query + required: false + schema: + type: boolean responses: 200: description: OK @@ -1046,6 +1051,11 @@ paths: required: false schema: $ref: '#/components/schemas/SortOrder' + - name: fts + in: query + required: false + schema: + type: boolean responses: 200: description: OK @@ -1218,6 +1228,12 @@ paths: required: false schema: $ref: '#/components/schemas/SortOrder' + - name: fts + in: query + required: false + schema: + type: boolean + responses: 200: description: OK @@ -1534,6 +1550,11 @@ paths: required: false schema: $ref: '#/components/schemas/SortOrder' + - name: fts + in: query + required: false + schema: + type: boolean responses: 200: description: OK @@ -2066,6 +2087,11 @@ paths: required: false schema: type: string + - name: fts + in: query + required: false + schema: + type: boolean responses: 200: description: OK @@ -2577,6 +2603,8 @@ components: - KAFKA_ACL_EDIT # create & delete ACLs - CLIENT_QUOTA_MANAGEMENT - GRAPHS_ENABLED + - FTS_ENABLED + - FTS_DEFAULT_ENABLED required: - name - status