This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 1fc4db92f fix(server): match literal search text in the audit,
metadata and alert queries (#7086)
1fc4db92f is described below
commit 1fc4db92f9bc88c289ef11a150186f94edbb8d70
Author: Loyal-Young <[email protected]>
AuthorDate: Mon Oct 26 21:21:11 2026 +0800
fix(server): match literal search text in the audit, metadata and alert
queries (#7086)
Integration batch of three same-family fixes by Loyal-Young: free-text
search in the audit repository (#7074), topic/group name search in the metadata
provider (#7083) and rule search in the alert repository (#7086) all passed the
raw term into a LIKE condition, so a '%' or '_' in the term acted as a
wildcard. Each escape is now one shared util (common/util/LikePatterns), which
also absorbs the fifth private copy in the AI conversation repository and the
one in QueryHistoryService. Th [...]
---
.../rocketmq/studio/common/util/LikePatterns.java | 38 +++++++++++++++++
.../instance/message/QueryHistoryService.java | 16 ++------
.../MybatisPlusAiConversationRepository.java | 13 +-----
.../ops/alert/MybatisPlusAlertRepository.java | 8 ++--
.../ops/audit/MybatisPlusAuditRepository.java | 48 +++++++++++-----------
.../provider/apache/RocketMQMetadataProvider.java | 13 ++++--
.../ops/alert/MybatisPlusAlertRepositoryTest.java | 36 ++++++++++++++++
.../ops/audit/MybatisPlusAuditRepositoryTest.java | 19 ++++++++-
.../apache/RocketMQMetadataProviderTest.java | 46 +++++++++++++++++++++
9 files changed, 182 insertions(+), 55 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/common/util/LikePatterns.java
b/server/src/main/java/org/apache/rocketmq/studio/common/util/LikePatterns.java
new file mode 100644
index 000000000..a8daf8def
--- /dev/null
+++
b/server/src/main/java/org/apache/rocketmq/studio/common/util/LikePatterns.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.common.util;
+
+/**
+ * Escapes the wildcards a MyBatis-Plus {@code like} condition would otherwise
honour, so a search
+ * term containing {@code %}, {@code _} or a backslash matches itself
literally instead of acting
+ * as a pattern. Four repositories used to carry private copies of this; the
escape is a property
+ * of the SQL LIKE operator, not of any one table.
+ */
+public final class LikePatterns {
+
+ private LikePatterns() {
+ }
+
+ /**
+ * @param value raw search term, may be null
+ * @return the escaped term, or null when {@code value} is null
+ */
+ public static String escape(String value) {
+ return value == null ? null
+ : value.replace("\\", "\\\\").replace("%", "\\%").replace("_",
"\\_");
+ }
+}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/message/QueryHistoryService.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/message/QueryHistoryService.java
index b7c18fa7c..cf4215b5f 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/message/QueryHistoryService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/message/QueryHistoryService.java
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.rocketmq.studio.common.util.LikePatterns;
import org.apache.rocketmq.studio.auth.AuthenticatedUserContext;
import org.apache.rocketmq.studio.persistence.entity.RmqMessageQuery;
import org.apache.rocketmq.studio.persistence.entity.RmqTraceQuery;
@@ -184,7 +185,7 @@ public class QueryHistoryService {
public PageResult<MessageQueryHistoryVO> listMessageQueries(String
clusterId, String queryType,
String
search, int page, int pageSize) {
- String pattern = escapeLike(search);
+ String pattern = LikePatterns.escape(search);
String queriedBy = AuthenticatedUserContext.currentUsernameOrSystem();
QueryWrapper<RmqMessageQuery> query = new
QueryWrapper<RmqMessageQuery>()
.eq(StringUtils.hasText(clusterId), "cluster_id", clusterId)
@@ -204,7 +205,7 @@ public class QueryHistoryService {
public PageResult<TraceQueryHistoryVO> listTraceQueries(String clusterId,
String search,
int page, int
pageSize) {
- String pattern = escapeLike(search);
+ String pattern = LikePatterns.escape(search);
String queriedBy = AuthenticatedUserContext.currentUsernameOrSystem();
QueryWrapper<RmqTraceQuery> query = new QueryWrapper<RmqTraceQuery>()
.eq(StringUtils.hasText(clusterId), "cluster_id", clusterId)
@@ -346,17 +347,6 @@ public class QueryHistoryService {
.queriedAt(query.getGmtCreate()).build();
}
- /**
- * Escapes LIKE wildcards so user-supplied search terms match literally
instead of being
- * interpreted as {@code %}/{@code _} patterns.
- */
- private static String escapeLike(String search) {
- if (!StringUtils.hasText(search)) {
- return search;
- }
- return search.replace("\\", "\\\\").replace("%", "\\%").replace("_",
"\\_");
- }
-
private static String normalizeOptional(String value) {
return StringUtils.hasText(value) ? value.trim() : null;
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/MybatisPlusAiConversationRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/MybatisPlusAiConversationRepository.java
index 359849b94..c95d7acd8 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/MybatisPlusAiConversationRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/MybatisPlusAiConversationRepository.java
@@ -20,6 +20,7 @@ import
com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
+import org.apache.rocketmq.studio.common.util.LikePatterns;
import org.apache.rocketmq.studio.common.domain.PageResult;
import org.apache.rocketmq.studio.persistence.entity.RmqAiConversation;
import org.apache.rocketmq.studio.persistence.mapper.RmqAiConversationMapper;
@@ -67,7 +68,7 @@ public class MybatisPlusAiConversationRepository implements
AiConversationReposi
QueryWrapper<RmqAiConversation> query = new
QueryWrapper<RmqAiConversation>()
.eq("owner", owner)
.eq(archived != null, "archived", archived)
- .like(StringUtils.hasText(search), "title", escapeLike(search))
+ .like(StringUtils.hasText(search), "title",
LikePatterns.escape(search))
// gmt_modified, not the project-wide gmt_create: a
conversation resumed today
// must float to the top. idx_ai_conversation_owner is built
for this ordering.
.orderByDesc("gmt_modified", "id");
@@ -123,14 +124,4 @@ public class MybatisPlusAiConversationRepository
implements AiConversationReposi
return conversationMapper.deleteByIds(ids);
}
- /**
- * Escapes LIKE wildcards so user-supplied search terms match literally.
Mirrors
- * QueryHistoryService.escapeLike; a shared util is tracked separately
upstream.
- */
- private static String escapeLike(String search) {
- if (!StringUtils.hasText(search)) {
- return search;
- }
- return search.replace("\\", "\\\\").replace("%", "\\%").replace("_",
"\\_");
- }
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
index 24098c320..1c6e7e13d 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepository.java
@@ -21,6 +21,7 @@ import
com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.rocketmq.studio.common.util.LikePatterns;
import org.apache.rocketmq.studio.common.domain.PageResult;
import org.apache.rocketmq.studio.common.domain.enums.AlertLevel;
import org.apache.rocketmq.studio.persistence.entity.RmqAlertRule;
@@ -82,19 +83,20 @@ public class MybatisPlusAlertRepository implements
AlertRepository {
private QueryWrapper<RmqAlertRule> ruleQuery(String search, Boolean
enabled) {
return new QueryWrapper<RmqAlertRule>()
- .like(StringUtils.hasText(search), "name", search)
+ .like(StringUtils.hasText(search), "name",
LikePatterns.escape(search))
.eq(enabled != null, "enabled", enabled)
.orderByAsc("name", "id");
}
@Override
public PageResult<AlertRuleVO> findRulesPage(AlertRuleQuery query) {
+ String literalSearch = StringUtils.hasText(query.search()) ?
LikePatterns.escape(query.search().trim()) : null;
QueryWrapper<RmqAlertRule> conditions = new
QueryWrapper<RmqAlertRule>()
.eq(query.enabled() != null, "enabled", query.enabled())
.and(StringUtils.hasText(query.search()), wrapper -> wrapper
- .like("name", query.search().trim())
+ .like("name", literalSearch)
.or()
- .like("metric", query.search().trim()))
+ .like("metric", literalSearch))
.orderByAsc("name")
.orderByAsc("id");
if (query.domain() == AlertDomain.BUSINESS) {
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepository.java
index 787ea5a37..b9fefd1db 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepository.java
@@ -18,6 +18,7 @@ package org.apache.rocketmq.studio.ops.audit;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.rocketmq.studio.common.util.LikePatterns;
import org.apache.rocketmq.studio.common.domain.PageResult;
import org.apache.rocketmq.studio.common.util.JdbcRowValues;
import org.apache.rocketmq.studio.persistence.entity.RmqOperationAudit;
@@ -189,29 +190,6 @@ public class MybatisPlusAuditRepository implements
AuditRepository {
.toList();
}
- /**
- * Single source of truth for the audit filters, shared by the paged list,
the summary and the
- * hotspot queries so a card can never disagree with the rows it describes.
- */
- private void applyFilters(QueryWrapper<RmqOperationAudit> query, String
search,
- String operator, String operationType, String
resourceType,
- String target, String clusterId, boolean
clusterIdMissing,
- LocalDateTime startDate, LocalDateTime endDate,
String result) {
- query.and(StringUtils.hasText(search), w -> w
- .like("operator", search)
- .or().like("resource_name", search)
- .or().like("detail", search))
- .eq(StringUtils.hasText(operator), "operator", operator)
- .eq(StringUtils.hasText(operationType), "operation",
operationType)
- .eq(StringUtils.hasText(resourceType), "resource_type",
resourceType)
- .eq(StringUtils.hasText(target), "resource_name", target)
- .eq(!clusterIdMissing && StringUtils.hasText(clusterId),
"cluster_id", clusterId)
- .and(clusterIdMissing, scope ->
scope.isNull("cluster_id").or().eq("cluster_id", ""))
- .ge(startDate != null, "gmt_create", startDate)
- .le(endDate != null, "gmt_create", endDate)
- .eq(StringUtils.hasText(result), "result", result);
- }
-
@Override
public void save(AuditRecordVO record) {
RmqOperationAudit entity = new RmqOperationAudit();
@@ -265,6 +243,30 @@ public class MybatisPlusAuditRepository implements
AuditRepository {
return totalDeleted;
}
+ /**
+ * Single source of truth for the audit filters, shared by the paged list,
the summary and the
+ * hotspot queries so a card can never disagree with the rows it describes.
+ */
+ private void applyFilters(QueryWrapper<RmqOperationAudit> query, String
search,
+ String operator, String operationType, String
resourceType,
+ String target, String clusterId, boolean
clusterIdMissing,
+ LocalDateTime startDate, LocalDateTime endDate,
String result) {
+ String searchPattern = StringUtils.hasText(search) ?
LikePatterns.escape(search) : null;
+ query.and(searchPattern != null, w -> w
+ .like("operator", searchPattern)
+ .or().like("resource_name", searchPattern)
+ .or().like("detail", searchPattern))
+ .eq(StringUtils.hasText(operator), "operator", operator)
+ .eq(StringUtils.hasText(operationType), "operation",
operationType)
+ .eq(StringUtils.hasText(resourceType), "resource_type",
resourceType)
+ .eq(StringUtils.hasText(target), "resource_name", target)
+ .eq(!clusterIdMissing && StringUtils.hasText(clusterId),
"cluster_id", clusterId)
+ .and(clusterIdMissing, scope ->
scope.isNull("cluster_id").or().eq("cluster_id", ""))
+ .ge(startDate != null, "gmt_create", startDate)
+ .le(endDate != null, "gmt_create", endDate)
+ .eq(StringUtils.hasText(result), "result", result);
+ }
+
private List<String> findDistinctValues(List<Map<String, Object>> rows,
String column) {
return rows.stream()
.map(row -> row.get(column))
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
index 456643478..415aca29b 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProvider.java
@@ -30,6 +30,7 @@ import
org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
import org.apache.rocketmq.remoting.protocol.route.QueueData;
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
+import org.apache.rocketmq.studio.common.util.LikePatterns;
import org.apache.rocketmq.studio.cluster.broker.MqAdminExtFactory;
import org.apache.rocketmq.studio.cluster.broker.RuntimeAdminClientResolver;
import org.apache.rocketmq.studio.common.exception.BusinessException;
@@ -144,6 +145,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
public List<TopicVO> listTopics(String instanceId, String clusterId,
String type, String search) {
String configuredCluster = StringUtils.hasText(instanceId)
? runtimeAdminClientResolver.configuredClusterName(instanceId)
: null;
+ String literalSearch = LikePatterns.escape(search);
LambdaQueryWrapper<RmqTopic> query = new LambdaQueryWrapper<RmqTopic>()
.and(instanceId != null, scope -> {
scope.eq(RmqTopic::getInstanceId,
normalizeMetadataScope(instanceId));
@@ -155,7 +157,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
})
.eq(StringUtils.hasText(clusterId), RmqTopic::getClusterId,
clusterId)
.eq(StringUtils.hasText(type), RmqTopic::getTopicType, type)
- .like(StringUtils.hasText(search), RmqTopic::getName, search)
+ .like(StringUtils.hasText(search), RmqTopic::getName,
literalSearch)
.orderByAsc(RmqTopic::getName);
List<TopicVO> result = new ArrayList<>();
@@ -173,6 +175,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
String search, int page, int pageSize) {
String configuredCluster = StringUtils.hasText(instanceId)
? runtimeAdminClientResolver.configuredClusterName(instanceId)
: null;
+ String literalSearch = LikePatterns.escape(search);
LambdaQueryWrapper<RmqTopic> query = new LambdaQueryWrapper<RmqTopic>()
.and(instanceId != null, scope -> {
scope.eq(RmqTopic::getInstanceId,
normalizeMetadataScope(instanceId));
@@ -184,7 +187,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
})
.eq(StringUtils.hasText(clusterId), RmqTopic::getClusterId,
clusterId)
.eq(StringUtils.hasText(type), RmqTopic::getTopicType, type)
- .like(StringUtils.hasText(search), RmqTopic::getName, search)
+ .like(StringUtils.hasText(search), RmqTopic::getName,
literalSearch)
.notIn(RmqTopic::getName, TopicValidator.getSystemTopicSet())
.notLikeRight(RmqTopic::getName, "rmq_sys_")
.notLikeRight(RmqTopic::getName, "%RETRY%")
@@ -243,6 +246,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
public List<ConsumerGroupVO> listConsumerGroups(String instanceId, String
clusterId, String search) {
String configuredCluster = StringUtils.hasText(instanceId)
? runtimeAdminClientResolver.configuredClusterName(instanceId)
: null;
+ String literalSearch = LikePatterns.escape(search);
LambdaQueryWrapper<RmqGroup> query = new LambdaQueryWrapper<RmqGroup>()
.and(instanceId != null, scope -> {
scope.eq(RmqGroup::getInstanceId,
normalizeMetadataScope(instanceId));
@@ -253,7 +257,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
}
})
.eq(StringUtils.hasText(clusterId), RmqGroup::getClusterId,
clusterId)
- .like(StringUtils.hasText(search), RmqGroup::getName, search)
+ .like(StringUtils.hasText(search), RmqGroup::getName,
literalSearch)
.orderByAsc(RmqGroup::getName);
List<ConsumerGroupVO> result = new ArrayList<>();
@@ -275,6 +279,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
String search, String subscriptionMode, int page, int pageSize) {
String configuredCluster = StringUtils.hasText(instanceId)
? runtimeAdminClientResolver.configuredClusterName(instanceId)
: null;
+ String literalSearch = LikePatterns.escape(search);
// message_model stores the subscription mode ("Push"/"Pop"); blank
and "ALL" mean no
// restriction, so the count and the page both describe the filtered
result set.
boolean modeRestricted = StringUtils.hasText(subscriptionMode)
@@ -290,7 +295,7 @@ public class RocketMQMetadataProvider implements
MetadataProvider {
}
})
.eq(StringUtils.hasText(clusterId), RmqGroup::getClusterId,
clusterId)
- .like(StringUtils.hasText(search), RmqGroup::getName, search)
+ .like(StringUtils.hasText(search), RmqGroup::getName,
literalSearch)
.and(modeRestricted, scope -> {
if (popOnly) {
scope.eq(RmqGroup::getMessageModel,
SubscriptionMode.Pop.name());
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
index 0ce8dc06d..dd389d179 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/MybatisPlusAlertRepositoryTest.java
@@ -189,6 +189,42 @@ class MybatisPlusAlertRepositoryTest {
verify(ruleMapper, never()).selectList(any());
}
+ @Test
+ void findRulePageShouldMatchLiteralSearchTextTest() {
+ Page<RmqAlertRule> mapperPage = new Page<RmqAlertRule>(1, 20)
+ .setRecords(List.of()).setTotal(0);
+ when(ruleMapper.selectPage(any(IPage.class),
any(Wrapper.class))).thenReturn(mapperPage);
+
+ assertThat(repository.findRulePage("broker_%\\east", true, 1,
20).getItems()).isEmpty();
+
+ ArgumentCaptor<Wrapper<RmqAlertRule>> queryCaptor =
ArgumentCaptor.forClass(Wrapper.class);
+ verify(ruleMapper).selectPage(any(IPage.class), queryCaptor.capture());
+ QueryWrapper<RmqAlertRule> query = (QueryWrapper<RmqAlertRule>)
queryCaptor.getValue();
+ assertThat(query.getSqlSegment()).contains("name LIKE", "enabled =");
+ assertThat(query.getParamNameValuePairs().values())
+ .contains("%broker\\_\\%\\\\east%", true);
+ }
+
+ @Test
+ void findRulesPageShouldMatchLiteralNameAndMetricTextTest() {
+ Page<RmqAlertRule> mapperPage = new Page<RmqAlertRule>(1, 20)
+ .setRecords(List.of()).setTotal(0);
+ when(ruleMapper.selectPage(any(IPage.class),
any(Wrapper.class))).thenReturn(mapperPage);
+
+ assertThat(repository.findRulesPage(
+ new AlertRuleQuery(AlertDomain.BUSINESS, " metric_50% ", true,
1, 20))
+ .getItems()).isEmpty();
+
+ ArgumentCaptor<Wrapper<RmqAlertRule>> queryCaptor =
ArgumentCaptor.forClass(Wrapper.class);
+ verify(ruleMapper).selectPage(any(IPage.class), queryCaptor.capture());
+ QueryWrapper<RmqAlertRule> query = (QueryWrapper<RmqAlertRule>)
queryCaptor.getValue();
+ assertThat(query.getSqlSegment()).contains("name LIKE", "metric LIKE",
"enabled =");
+ assertThat(query.getParamNameValuePairs().values())
+ .contains("%metric\\_50\\%%", true);
+ assertThat(query.getParamNameValuePairs().values().stream()
+ .filter("%metric\\_50\\%%"::equals)).hasSize(2);
+ }
+
@Test
void findRuleByIdShouldUsePrimaryKeyLookupWithoutFullListRead() {
RmqAlertRule entity = new RmqAlertRule();
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepositoryTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepositoryTest.java
index 12187b675..0e8d62599 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepositoryTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/audit/MybatisPlusAuditRepositoryTest.java
@@ -372,4 +372,21 @@ class MybatisPlusAuditRepositoryTest {
return result;
}
-}
+ @Test
+ void findPageTreatsSearchWildcardsAsLiteralAuditTextTest() {
+ when(auditMapper.selectPage(any(IPage.class), any(Wrapper.class)))
+ .thenReturn(new Page<RmqOperationAudit>(1,
20).setRecords(List.of()).setTotal(0));
+
+ repository.findPage("topic_%", null, null, null, null, null, false,
+ null, null, null, 1, 20);
+
+ ArgumentCaptor<Wrapper<RmqOperationAudit>> queryCaptor =
ArgumentCaptor.forClass(Wrapper.class);
+ verify(auditMapper).selectPage(any(IPage.class),
queryCaptor.capture());
+ assertThat(((QueryWrapper<RmqOperationAudit>)
queryCaptor.getValue()).getSqlSegment())
+ .contains("operator LIKE", "resource_name LIKE", "detail
LIKE");
+ assertThat(((QueryWrapper<RmqOperationAudit>) queryCaptor.getValue())
+ .getParamNameValuePairs().values())
+ .containsExactlyInAnyOrder("%topic\\_\\%%", "%topic\\_\\%%",
"%topic\\_\\%%");
+ }
+
+}
\ No newline at end of file
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
index 03de6cfa7..6de3d9da9 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQMetadataProviderTest.java
@@ -290,6 +290,52 @@ class RocketMQMetadataProviderTest {
.contains("message_model IS NULL", "message_model <>");
}
+ @Test
+ void topicListAndPageShouldSearchLiteralNameCharactersTest() {
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqTopic.class);
+ when(topicMapper.selectList(any())).thenReturn(List.of());
+ Page<RmqTopic> databasePage = new Page<>(1, 20, 0);
+ databasePage.setRecords(List.of());
+ when(topicMapper.selectPage(any(Page.class),
any(LambdaQueryWrapper.class))).thenReturn(databasePage);
+ RocketMQMetadataProvider provider = newProvider();
+
+ assertThat(provider.listTopics(null, null, null,
"orders_%\\west")).isEmpty();
+ assertThat(provider.listTopicsPage(null, null, null, "orders_%\\west",
1, 20).getItems())
+ .isEmpty();
+
+ ArgumentCaptor<LambdaQueryWrapper<RmqTopic>> listQuery =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
+ ArgumentCaptor<LambdaQueryWrapper<RmqTopic>> pageQuery =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
+ verify(topicMapper).selectList(listQuery.capture());
+ verify(topicMapper).selectPage(any(Page.class), pageQuery.capture());
+ for (LambdaQueryWrapper<RmqTopic> query :
List.of(listQuery.getValue(), pageQuery.getValue())) {
+ assertThat(query.getSqlSegment()).contains("name LIKE");
+
assertThat(query.getParamNameValuePairs().values()).contains("%orders\\_\\%\\\\west%");
+ }
+ }
+
+ @Test
+ void groupListAndPageShouldSearchLiteralNameCharactersTest() {
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqGroup.class);
+ when(groupMapper.selectList(any())).thenReturn(List.of());
+ Page<RmqGroup> databasePage = new Page<>(1, 20, 0);
+ databasePage.setRecords(List.of());
+ when(groupMapper.selectPage(any(Page.class),
any(LambdaQueryWrapper.class))).thenReturn(databasePage);
+ RocketMQMetadataProvider provider = newProvider();
+
+ assertThat(provider.listConsumerGroups(null, null,
"orders_%\\west")).isEmpty();
+ assertThat(provider.listConsumerGroupsPage(null, null,
"orders_%\\west", 1, 20).getItems())
+ .isEmpty();
+
+ ArgumentCaptor<LambdaQueryWrapper<RmqGroup>> listQuery =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
+ ArgumentCaptor<LambdaQueryWrapper<RmqGroup>> pageQuery =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
+ verify(groupMapper).selectList(listQuery.capture());
+ verify(groupMapper).selectPage(any(Page.class), pageQuery.capture());
+ for (LambdaQueryWrapper<RmqGroup> query :
List.of(listQuery.getValue(), pageQuery.getValue())) {
+ assertThat(query.getSqlSegment()).contains("name LIKE");
+
assertThat(query.getParamNameValuePairs().values()).contains("%orders\\_\\%\\\\west%");
+ }
+ }
+
@Test
void getTopicRoutesShouldUseSelectedInstanceRuntimeClient() {
List<BrokerRouteVO> routes =
List.of(BrokerRouteVO.builder().brokerName("broker-a").build());