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();

Reply via email to