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 74c9ea91c fix(topic): reject updates for missing topics (#4538)
74c9ea91c is described below
commit 74c9ea91c8259e56e1599399400b574cf03b7307
Author: zmuxuny <[email protected]>
AuthorDate: Mon Sep 21 21:01:55 2026 +0800
fix(topic): reject updates for missing topics (#4538)
`RocketMQAdminClientImpl.updateTopic` scoped its `rmq_topic` lookup to the
resolved cluster and instance, but carried on when the lookup found nothing: it
fell back to 8 write / 8 read queues and `TopicPerm.RW`, applied whatever type
the request carried, and called `createAndUpdateTopicConfig` on every master in
the cluster. That admin call is an update-or-create, so an update for a topic
Studio had no metadata row for created it on the brokers — and the subsequent
`rmq_topic` write i [...]
`updateTopic` now throws `BusinessException(404, "Topic not found: ...")`
as soon as the scoped lookup returns null, before any broker mutation. Upsert
semantics are untouched: the MCP topic-update handler resolves a missing topic
through `findTopic` and routes it to `createTopic`.
Fixes #4537
---
.../provider/apache/RocketMQAdminClientImpl.java | 3 +++
.../apache/RocketMQAdminClientImplTest.java | 30 ++++++++++++++++++++--
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImpl.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImpl.java
index a95ede465..16553a9ac 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImpl.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImpl.java
@@ -362,6 +362,9 @@ public class RocketMQAdminClientImpl implements AdminClient
{
.eq(RmqTopic::getClusterId, clusterName)
.eq(RmqTopic::getInstanceId,
metadataScope(instanceId))
.eq(RmqTopic::getName, topicName));
+ if (existing == null) {
+ throw new BusinessException(404, "Topic not found: " +
topicName);
+ }
// Preserve the existing queue counts when the update request
does not change them,
// matching the perm semantics below; defaulting to 8 would
silently resize the
// topic on partial updates (e.g. perm or remark only).
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImplTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImplTest.java
index 49114b2b0..8fa9262fe 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImplTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQAdminClientImplTest.java
@@ -791,7 +791,11 @@ class RocketMQAdminClientImplTest {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqTopic.class);
DefaultMQAdminExt selectedAdmin =
org.mockito.Mockito.mock(DefaultMQAdminExt.class);
when(selectedAdmin.examineBrokerClusterInfo()).thenReturn(clusterInfoWithMaster());
- when(topicMapper.selectOne(any())).thenReturn(null);
+ RmqTopic created = new RmqTopic();
+ created.setName("topicA");
+ created.setInstanceId("open-source-local");
+ created.setClusterId("cluster-1");
+ when(topicMapper.selectOne(any())).thenReturn(null, null, created);
doNothing().when(selectedAdmin).createAndUpdateTopicConfig(anyString(),
any(TopicConfig.class));
when(runtimeAdminClientResolver.execute(org.mockito.ArgumentMatchers.eq("open-source-local"),
any()))
.thenAnswer(invocation ->
invocation.<MqAdminExtFactory.AdminAction<Object>>getArgument(1)
@@ -810,6 +814,24 @@ class RocketMQAdminClientImplTest {
verify(adminExt, never()).createAndUpdateTopicConfig(anyString(),
any(TopicConfig.class));
}
+ @Test
+ void updateTopicShouldRejectMissingMetadataBeforeBrokerMutationTest()
throws Exception {
+ TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqTopic.class);
+
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithMaster());
+ when(topicMapper.selectOne(any())).thenReturn(null);
+
+ TopicVO topic = new TopicVO();
+ topic.setName("missing-topic");
+
+ assertThatThrownBy(() -> adminClient.updateTopic(topic))
+ .isInstanceOfSatisfying(BusinessException.class, error -> {
+ assertThat(error.getCode()).isEqualTo(404);
+ assertThat(error.getMessage()).isEqualTo("Topic not found:
missing-topic");
+ });
+
+ verify(adminExt, never()).createAndUpdateTopicConfig(anyString(),
any(TopicConfig.class));
+ }
+
@Test
void updateTopicPersistsTypeAndRemark() throws Exception {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqTopic.class);
@@ -885,7 +907,11 @@ class RocketMQAdminClientImplTest {
void topicWritesSendMessageTypeAttributeToBroker() throws Exception {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new
MybatisConfiguration(), ""), RmqTopic.class);
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithMaster());
- when(topicMapper.selectOne(any())).thenReturn(null);
+ RmqTopic created = new RmqTopic();
+ created.setName("orders");
+ created.setClusterId("cluster-1");
+ created.setTopicType(TopicType.FIFO.name());
+ when(topicMapper.selectOne(any())).thenReturn(null, null, created);
doNothing().when(adminExt).createAndUpdateTopicConfig(anyString(),
any(TopicConfig.class));
TopicVO topic = new TopicVO();