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

commit 6c03e140dc4fd38ccfc4253b45fbe38a4543f64a
Author: wizcraft_kris <[email protected]>
AuthorDate: Fri Jul 24 13:43:09 2026 +0800

    fix: generate data source keys on creation (#509)
    
    Fix ConcurrentHashMap NPE when creating data sources with null key by 
auto-generating UUID in service layer.
---
 .../rocketmq/studio/settings/SettingsService.java  |  2 +
 .../settings/InMemorySettingsRepositoryTest.java   | 54 ++++++++++++++++++++++
 .../studio/settings/SettingsServiceTest.java       | 22 ++++++---
 3 files changed, 72 insertions(+), 6 deletions(-)

diff --git 
a/server/src/main/java/com/rocketmq/studio/settings/SettingsService.java 
b/server/src/main/java/com/rocketmq/studio/settings/SettingsService.java
index 1f128769..5505d27f 100644
--- a/server/src/main/java/com/rocketmq/studio/settings/SettingsService.java
+++ b/server/src/main/java/com/rocketmq/studio/settings/SettingsService.java
@@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.UUID;
 
 @Slf4j
 @Service
@@ -50,6 +51,7 @@ public class SettingsService {
 
     public DataSourceVO createDataSource(DataSourceVO dataSource) {
         log.info("Creating data source: {}", dataSource.getName());
+        dataSource.setKey(UUID.randomUUID().toString());
         return settingsRepository.saveDataSource(dataSource);
     }
 
diff --git 
a/server/src/test/java/com/rocketmq/studio/settings/InMemorySettingsRepositoryTest.java
 
b/server/src/test/java/com/rocketmq/studio/settings/InMemorySettingsRepositoryTest.java
new file mode 100644
index 00000000..d1d06f60
--- /dev/null
+++ 
b/server/src/test/java/com/rocketmq/studio/settings/InMemorySettingsRepositoryTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.rocketmq.studio.settings;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class InMemorySettingsRepositoryTest {
+
+    private final InMemorySettingsRepository repository = new 
InMemorySettingsRepository();
+
+    @Test
+    void saveDataSourceShouldSupportCreateUpdateAndDeleteByKey() {
+        DataSourceVO dataSource = DataSourceVO.builder()
+                .key("source-1")
+                .name("Prometheus")
+                .type("Prometheus")
+                .url("http://localhost:9090";)
+                .build();
+
+        repository.saveDataSource(dataSource);
+
+        
assertThat(repository.findDataSourceByKey("source-1")).containsSame(dataSource);
+        
assertThat(repository.findAllDataSources()).containsExactly(dataSource);
+
+        dataSource.setName("Updated Prometheus");
+        repository.saveDataSource(dataSource);
+
+        assertThat(repository.findDataSourceByKey("source-1"))
+                .get()
+                .extracting(DataSourceVO::getName)
+                .isEqualTo("Updated Prometheus");
+
+        repository.deleteDataSource("source-1");
+
+        assertThat(repository.findDataSourceByKey("source-1")).isEmpty();
+        assertThat(repository.findAllDataSources()).isEmpty();
+    }
+}
diff --git 
a/server/src/test/java/com/rocketmq/studio/settings/SettingsServiceTest.java 
b/server/src/test/java/com/rocketmq/studio/settings/SettingsServiceTest.java
index 331197e3..430021af 100644
--- a/server/src/test/java/com/rocketmq/studio/settings/SettingsServiceTest.java
+++ b/server/src/test/java/com/rocketmq/studio/settings/SettingsServiceTest.java
@@ -108,18 +108,28 @@ class SettingsServiceTest {
     }
 
     @Test
-    void createDataSourceShouldDelegateToRepository() {
+    void createDataSourceShouldAssignKeyBeforeSaving() {
         DataSourceVO input = DataSourceVO.builder().name("New 
DS").type("rocketmq")
                 .url("new-host:9876").build();
-        DataSourceVO saved = DataSourceVO.builder().key("ds-new").name("New 
DS").type("rocketmq")
-                .url("new-host:9876").status("connected").build();
-        
when(settingsRepository.saveDataSource(any(DataSourceVO.class))).thenReturn(saved);
+        when(settingsRepository.saveDataSource(any(DataSourceVO.class)))
+                .thenAnswer(invocation -> invocation.getArgument(0));
 
         DataSourceVO result = settingsService.createDataSource(input);
 
-        assertThat(result.getKey()).isEqualTo("ds-new");
+        assertThat(result.getKey()).isNotBlank();
         assertThat(result.getName()).isEqualTo("New DS");
-        assertThat(result.getStatus()).isEqualTo("connected");
+        verify(settingsRepository).saveDataSource(input);
+    }
+
+    @Test
+    void createDataSourceShouldReplaceClientProvidedKey() {
+        DataSourceVO input = 
DataSourceVO.builder().key("existing-key").name("New DS").build();
+        when(settingsRepository.saveDataSource(any(DataSourceVO.class)))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        DataSourceVO result = settingsService.createDataSource(input);
+
+        assertThat(result.getKey()).isNotBlank().isNotEqualTo("existing-key");
         verify(settingsRepository).saveDataSource(input);
     }
 

Reply via email to