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 b73fbe77 fix(settings): harden persisted settings integrity (#1475)
b73fbe77 is described below

commit b73fbe77b3523cf8dbabd6401467e8fc5ba0114f
Author: aias00 <[email protected]>
AuthorDate: Tue Aug 11 15:33:14 2026 +0800

    fix(settings): harden persisted settings integrity (#1475)
    
    * fix(settings): surface corrupt persisted settings
    
    Signed-off-by: liuhy <[email protected]>
    
    * fix(settings): surface corrupt persisted data sources
    
    Signed-off-by: liuhy <[email protected]>
    
    ---------
    
    Signed-off-by: liuhy <[email protected]>
---
 .../persistence/MybatisPlusSettingsRepository.java | 16 +---
 .../MybatisPlusSettingsRepositoryTest.java         | 86 ++++++++++++++++++++++
 2 files changed, 89 insertions(+), 13 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepository.java
 
b/server/src/main/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepository.java
index ad887969..45e63a45 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepository.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepository.java
@@ -23,6 +23,7 @@ import 
org.apache.rocketmq.studio.persistence.entity.RmqDataSource;
 import org.apache.rocketmq.studio.persistence.entity.RmqSettings;
 import org.apache.rocketmq.studio.persistence.mapper.RmqDataSourceMapper;
 import org.apache.rocketmq.studio.persistence.mapper.RmqSettingsMapper;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
 import org.apache.rocketmq.studio.settings.DataSourceVO;
 import org.apache.rocketmq.studio.settings.GeneralSettingsVO;
 import org.apache.rocketmq.studio.settings.SettingsRepository;
@@ -73,18 +74,7 @@ public class MybatisPlusSettingsRepository implements 
SettingsRepository {
             return objectMapper.readValue(entity.getJson(), 
GeneralSettingsVO.class);
         } catch (JsonProcessingException e) {
             log.error("Failed to deserialize general settings", e);
-            return GeneralSettingsVO.builder()
-                    .theme("system")
-                    .compact(false)
-                    .desktopNotify(true)
-                    .notifySound(false)
-                    .sessionTimeout(30)
-                    .requireLogin(false)
-                    .llmProvider("openai")
-                    .apiKey("")
-                    .model("gpt-4")
-                    .baseUrl("")
-                    .build();
+            throw new BusinessException(500, "Persisted general settings are 
invalid");
         }
     }
 
@@ -162,7 +152,7 @@ public class MybatisPlusSettingsRepository implements 
SettingsRepository {
             return vo;
         } catch (JsonProcessingException e) {
             log.error("Failed to deserialize data source: {}", 
entity.getDsKey(), e);
-            return DataSourceVO.builder().key(entity.getDsKey()).build();
+            throw new BusinessException(500, "Persisted data source is 
invalid: " + entity.getDsKey());
         }
     }
 
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepositoryTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepositoryTest.java
new file mode 100644
index 00000000..84ad68d4
--- /dev/null
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/persistence/MybatisPlusSettingsRepositoryTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package org.apache.rocketmq.studio.persistence;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
+import org.apache.rocketmq.studio.persistence.entity.RmqDataSource;
+import org.apache.rocketmq.studio.persistence.entity.RmqSettings;
+import org.apache.rocketmq.studio.persistence.mapper.RmqDataSourceMapper;
+import org.apache.rocketmq.studio.persistence.mapper.RmqSettingsMapper;
+import org.apache.rocketmq.studio.settings.GeneralSettingsVO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class MybatisPlusSettingsRepositoryTest {
+
+    private RmqSettingsMapper settingsMapper;
+    private RmqDataSourceMapper dataSourceMapper;
+    private MybatisPlusSettingsRepository repository;
+
+    @BeforeEach
+    void setUp() {
+        settingsMapper = mock(RmqSettingsMapper.class);
+        dataSourceMapper = mock(RmqDataSourceMapper.class);
+        repository = new MybatisPlusSettingsRepository(settingsMapper, 
dataSourceMapper,
+                new ObjectMapper());
+    }
+
+    @Test
+    void shouldReturnDefaultsWhenGeneralSettingsDoNotExist() {
+        when(settingsMapper.selectById("singleton")).thenReturn(null);
+
+        GeneralSettingsVO settings = repository.loadGeneralSettings();
+
+        assertThat(settings.getTheme()).isEqualTo("system");
+        assertThat(settings.isRequireLogin()).isFalse();
+    }
+
+    @Test
+    void shouldRejectCorruptPersistedGeneralSettings() {
+        RmqSettings settings = new RmqSettings();
+        settings.setJson("{not-json");
+        when(settingsMapper.selectById("singleton")).thenReturn(settings);
+
+        assertThatThrownBy(repository::loadGeneralSettings)
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Persisted general settings are invalid")
+                .extracting("code")
+                .isEqualTo(500);
+    }
+
+    @Test
+    void shouldReadValidPersistedGeneralSettings() {
+        RmqSettings settings = new RmqSettings();
+        settings.setJson("{\"theme\":\"dark\",\"requireLogin\":true}");
+        when(settingsMapper.selectById("singleton")).thenReturn(settings);
+
+        GeneralSettingsVO loaded = repository.loadGeneralSettings();
+
+        assertThat(loaded.getTheme()).isEqualTo("dark");
+        assertThat(loaded.isRequireLogin()).isTrue();
+    }
+
+    @Test
+    void shouldRejectCorruptPersistedDataSource() {
+        RmqDataSource dataSource = new RmqDataSource();
+        dataSource.setDsKey("metrics-prod");
+        dataSource.setJson("{not-json");
+        
when(dataSourceMapper.selectById("metrics-prod")).thenReturn(dataSource);
+
+        assertThatThrownBy(() -> 
repository.findDataSourceByKey("metrics-prod"))
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("Persisted data source is invalid: metrics-prod")
+                .extracting("code")
+                .isEqualTo(500);
+    }
+}

Reply via email to