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 87c4c7ff7 fix(topic): allow a blank update to clear the persisted 
topic remark (#4275)
87c4c7ff7 is described below

commit 87c4c7ff7679c36584dcba5f293de23c373d7df5
Author: btlqql <[email protected]>
AuthorDate: Tue Sep 15 20:25:11 2026 +0800

    fix(topic): allow a blank update to clear the persisted topic remark (#4275)
    
    `RocketMQAdminClientImpl.updateTopic` only wrote the cached row's remark 
when the submitted value
    had text:
    
        if (StringUtils.hasText(topic.getRemark())) {
            existing.setRemark(topic.getRemark());
        }
    
    so a blank submission behaved exactly like an omitted one. `TopicVO.remark` 
/ `UpdateTopicDTO.remark`
    are plain nullable strings with no validation and there is no separate 
clear flag (unlike
    `clearApiKey` / `clearDingtalkSigningSecret` in general settings), so a 
blank remark is the only way
    a caller can say "this topic has no remark" - and that value was discarded. 
The `rmq_instance_topic`
    row therefore keeps the previous remark while the endpoint answers 200, and 
the topic list shows the
    old text again on the next load.
    
    Clearing the column is a two-part change. `updateById` omits null entity 
fields
    (`FieldStrategy.NOT_NULL`), the same mechanism already handled for the ACL 
user/rule columns in
    #3342, so the cleared remark is assigned explicitly instead of relying on 
the entity write. An
    omitted remark still keeps the stored value, which is the partial-update 
behaviour the existing
    comment in this method describes.
    
    The response now reports the persisted remark instead of the submitted one, 
so a clear and an
    omitted value cannot be reported as a state the database does not hold.
    
    `RocketMQAdminClientImplTest` pins both directions: a blank remark clears 
the column through an
    explicit assignment (and the returned topic is empty), while an omitted 
remark keeps the stored value
    and issues no assignment at all.
    
    Fixes #4272
    
    
    (cherry picked from commit 925958f7420dbe3bd543953b1c3c1c46147f47eb)
    
    Signed-off-by: btlqql <[email protected]>
---
 .../provider/apache/RocketMQAdminClientImpl.java   | 20 ++++++++-
 .../apache/RocketMQAdminClientImplTest.java        | 51 ++++++++++++++++++++++
 2 files changed, 69 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 2e3d0dbca..f1131b7c4 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
@@ -53,6 +53,7 @@ import 
org.apache.rocketmq.studio.persistence.mapper.RmqGroupMapper;
 import org.apache.rocketmq.studio.persistence.mapper.RmqTopicMapper;
 import org.apache.rocketmq.tools.admin.MQAdminExt;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import org.springframework.context.annotation.Primary;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
@@ -383,11 +384,21 @@ public class RocketMQAdminClientImpl implements 
AdminClient {
                     if (topic.getType() != null) {
                         existing.setTopicType(topic.getType().name());
                     }
-                    if (StringUtils.hasText(topic.getRemark())) {
-                        existing.setRemark(topic.getRemark());
+                    // A null remark was not submitted and keeps the stored 
value; a submitted blank
+                    // remark is an explicit clear, so it must persist as an 
absent remark.
+                    boolean clearRemark = topic.getRemark() != null && 
!StringUtils.hasText(topic.getRemark());
+                    if (topic.getRemark() != null) {
+                        existing.setRemark(clearRemark ? null : 
topic.getRemark());
                     }
                     existing.setGmtModified(LocalDateTime.now());
                     topicMapper.updateById(existing);
+                    if (clearRemark) {
+                        // updateById omits null entity fields, so the cleared 
remark has to be
+                        // assigned explicitly instead of silently retaining 
the stored value.
+                        topicMapper.update(null, new UpdateWrapper<RmqTopic>()
+                                .eq("id", existing.getId())
+                                .set("remark", null));
+                    }
                 }
 
                 recordAudit("UPDATE_TOPIC", topicName,
@@ -396,6 +407,11 @@ public class RocketMQAdminClientImpl implements 
AdminClient {
                 topic.setId(existing == null ? null : existing.getId());
                 topic.setWriteQueues(writeQueues);
                 topic.setReadQueues(readQueues);
+                if (existing != null) {
+                    // Report the persisted remark so a clear or an omitted 
remark cannot be
+                    // mistaken for a value the update did not write.
+                    topic.setRemark(existing.getRemark());
+                }
                 return topic;
             } catch (BusinessException e) {
                 recordAudit("UPDATE_TOPIC", topicName, e.getMessage(), 
"FAILED");
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 b07bddb62..487cc16ac 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
@@ -12,6 +12,7 @@ package org.apache.rocketmq.studio.provider.apache;
 
 import com.baomidou.mybatisplus.core.MybatisConfiguration;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
 import org.apache.ibatis.builder.MapperBuilderAssistant;
 import org.apache.rocketmq.client.exception.MQBrokerException;
@@ -704,6 +705,56 @@ class RocketMQAdminClientImplTest {
         verify(topicMapper).updateById(existing);
     }
 
+    @Test
+    void updateTopicClearsTheStoredRemarkWhenTheRequestSubmitsABlankRemark() 
throws Exception {
+        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new 
MybatisConfiguration(), ""), RmqTopic.class);
+        RmqTopic existing = new RmqTopic();
+        existing.setId(7L);
+        existing.setTopicType(TopicType.NORMAL.name());
+        existing.setRemark("old remark");
+        existing.setWriteQueueNums(8);
+        existing.setReadQueueNums(8);
+        
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithMaster());
+        when(topicMapper.selectOne(any())).thenReturn(existing);
+        when(topicMapper.update(isNull(), 
any(UpdateWrapper.class))).thenReturn(1);
+
+        TopicVO topic = new TopicVO();
+        topic.setName("orders");
+        topic.setRemark("");
+
+        TopicVO updated = adminClient.updateTopic(topic);
+
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<UpdateWrapper> captor = 
ArgumentCaptor.forClass(UpdateWrapper.class);
+        verify(topicMapper).update(isNull(), captor.capture());
+        assertThat(captor.getValue().getSqlSet()).contains("remark");
+        
assertThat(captor.getValue().getParamNameValuePairs()).containsValue(null);
+        assertThat(existing.getRemark()).isNull();
+        assertThat(updated.getRemark()).isNull();
+    }
+
+    @Test
+    void updateTopicKeepsTheStoredRemarkWhenTheRequestOmitsIt() throws 
Exception {
+        TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new 
MybatisConfiguration(), ""), RmqTopic.class);
+        RmqTopic existing = new RmqTopic();
+        existing.setId(7L);
+        existing.setTopicType(TopicType.NORMAL.name());
+        existing.setRemark("old remark");
+        existing.setWriteQueueNums(8);
+        existing.setReadQueueNums(8);
+        
when(adminExt.examineBrokerClusterInfo()).thenReturn(clusterInfoWithMaster());
+        when(topicMapper.selectOne(any())).thenReturn(existing);
+
+        TopicVO topic = new TopicVO();
+        topic.setName("orders");
+
+        TopicVO updated = adminClient.updateTopic(topic);
+
+        verify(topicMapper, never()).update(any(), any());
+        assertThat(existing.getRemark()).isEqualTo("old remark");
+        assertThat(updated.getRemark()).isEqualTo("old remark");
+    }
+
     @Test
     void topicWritesSendMessageTypeAttributeToBroker() throws Exception {
         TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new 
MybatisConfiguration(), ""), RmqTopic.class);

Reply via email to