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 d678c0b9 feat(server): use the instance name as the immutable instance
ID (#2050)
d678c0b9 is described below
commit d678c0b988bb751aa506029e281e6714b656472a
Author: lizhimins <[email protected]>
AuthorDate: Thu Aug 13 11:51:54 2026 +0800
feat(server): use the instance name as the immutable instance ID (#2050)
Stop generating UUIDs for instances: the user-supplied name (unique,
max 64 chars) becomes the primary key of rmq_instance and is immutable
after creation. All lookups go through InstanceRepository#findByIdentifier,
which prefers the instance ID and falls back to legacy primary keys,
and existing UUID rows are migrated by the two new upgrade scripts.
Also bump the Aliyun resource-count page size to the OpenAPI minimum.
---
deploy/mysql/upgrade-instance-id-pk.sql | 42 ++++++++++++++++++++++
deploy/mysql/upgrade-instance-name-uk.sql | 36 +++++++++++++++++++
server/README.md | 12 +++++++
.../cluster/broker/RuntimeAdminClientResolver.java | 2 +-
.../studio/instance/InstanceRepository.java | 13 +++++++
.../rocketmq/studio/instance/InstanceService.java | 26 +++++++++++---
.../instance/MybatisPlusInstanceRepository.java | 13 +++++++
.../rocketmq/studio/instance/acl/AclService.java | 2 +-
.../studio/provider/InstanceProviderRegistry.java | 2 +-
.../provider/alibaba/AliyunInstanceProvider.java | 4 +--
.../provider/tencent/TencentInstanceProvider.java | 2 +-
server/src/main/resources/db/schema.sql | 3 +-
.../broker/RuntimeAdminClientResolverTest.java | 14 ++++----
.../studio/instance/InstanceServiceTest.java | 28 ++++++++-------
.../studio/instance/acl/AclServiceTest.java | 4 +--
.../provider/InstanceProviderRegistryTest.java | 6 ++--
.../alibaba/AliyunInstanceProviderTest.java | 18 +++++-----
.../tencent/TencentInstanceProviderTest.java | 2 +-
18 files changed, 183 insertions(+), 46 deletions(-)
diff --git a/deploy/mysql/upgrade-instance-id-pk.sql
b/deploy/mysql/upgrade-instance-id-pk.sql
new file mode 100644
index 00000000..284a8b5a
--- /dev/null
+++ b/deploy/mysql/upgrade-instance-id-pk.sql
@@ -0,0 +1,42 @@
+-- deploy/mysql/upgrade-instance-id-pk.sql
+-- 存量 MySQL 数据卷增量迁移(2026-08-12):废弃实例 UUID 主键,rmq_instance.id 改写为实例 ID(name)。
+-- 背景:系统不再使用 UUID 作为实例标识;实例 ID(原 name 字段,全局唯一、创建后不可变)
+-- 直接作为主键,URL / API / 存储一律走实例 ID。
+-- 适用:数据卷已初始化、docker-entrypoint-initdb.d 不会再执行的存量部署。
+-- 全新创建的实例 id 即实例 ID,无需本脚本。
+-- 幂等:可重复执行(所有语句带 id <> name 条件)。
+--
+-- ⚠️ 第 3 步会改写 rmq_instance 主键值,执行前建议备份:
+-- docker exec rocketmq-studio-mysql sh -c 'exec mysqldump -uroot
-pstudio123 rocketmq' > backup.sql
+--
+-- 用法(远程容器内执行):
+-- docker exec -i rocketmq-studio-mysql mysql -uroot -pstudio123 rocketmq <
upgrade-instance-id-pk.sql
+
+SET NAMES utf8mb4;
+
+-- 1. 子表引用列先改(必须在 rmq_instance 主键改写之前执行)
+UPDATE rmq_topic t
+JOIN rmq_instance i ON t.instance_id = i.id AND i.id <> i.name
+SET t.instance_id = i.name;
+
+UPDATE rmq_group g
+JOIN rmq_instance i ON g.instance_id = i.id AND i.id <> i.name
+SET g.instance_id = i.name;
+
+UPDATE rmq_acl_rule r
+JOIN rmq_instance i ON r.scope = i.id AND i.id <> i.name
+SET r.scope = i.name;
+
+-- 2. 逗号分隔 / JSON 内容按实例逐个替换
+UPDATE rmq_acl_user u
+JOIN rmq_instance i ON i.id <> i.name
+SET u.clusters = REPLACE(u.clusters, i.id, i.name)
+WHERE u.clusters LIKE CONCAT('%', i.id, '%');
+
+UPDATE rmq_data_source d
+JOIN rmq_instance i ON i.id <> i.name
+SET d.json = REPLACE(d.json, i.id, i.name)
+WHERE d.json LIKE CONCAT('%', i.id, '%');
+
+-- 3. 最后改写主键:id = 实例 ID
+UPDATE rmq_instance SET id = name WHERE id <> name;
diff --git a/deploy/mysql/upgrade-instance-name-uk.sql
b/deploy/mysql/upgrade-instance-name-uk.sql
new file mode 100644
index 00000000..40f796b8
--- /dev/null
+++ b/deploy/mysql/upgrade-instance-name-uk.sql
@@ -0,0 +1,36 @@
+-- deploy/mysql/upgrade-instance-name-uk.sql
+-- 存量 MySQL 数据卷增量迁移(2026-08-12):rmq_instance.name 全局唯一。
+-- 背景:实例名(instanceId 概念)在 开源/阿里云/腾讯云 之间不重复,作为业务唯一标识;
+-- 此前无唯一约束,重复提交会把同一云实例加入两次。
+-- 适用:数据卷已初始化、docker-entrypoint-initdb.d 不会再执行的存量部署。
+-- 全新数据卷由 server/src/main/resources/db/schema.sql 直接带上 uk_instance_name。
+-- 幂等:可重复执行。
+--
+-- ⚠️ 会删除同名重复实例(保留 created_at 最早的一条)。执行前可先用下面的查询核对重复:
+-- SELECT name, COUNT(*) FROM rmq_instance GROUP BY name HAVING COUNT(*) > 1;
+--
+-- 用法(远程容器内执行):
+-- docker exec -i rocketmq-studio-mysql mysql -uroot -pstudio123 rocketmq <
upgrade-instance-name-uk.sql
+
+SET NAMES utf8mb4;
+
+-- 1. 清理同名重复(保留 created_at 最早,id 最小作为并列时的决胜)
+DELETE i FROM rmq_instance i
+JOIN rmq_instance k
+ ON k.name = i.name
+ AND (k.created_at < i.created_at
+ OR (k.created_at = i.created_at AND k.id < i.id));
+
+-- 2. 追加唯一键(仅当不存在时)
+SET @uk_exists := (
+ SELECT COUNT(*) FROM information_schema.statistics
+ WHERE table_schema = DATABASE()
+ AND table_name = 'rmq_instance'
+ AND index_name = 'uk_instance_name'
+);
+SET @uk_sql := IF(@uk_exists = 0,
+ 'ALTER TABLE rmq_instance ADD UNIQUE KEY uk_instance_name (name)',
+ 'SELECT ''uk_instance_name already exists'' AS msg');
+PREPARE stmt FROM @uk_sql;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
diff --git a/server/README.md b/server/README.md
index ffca74e3..46ff1369 100644
--- a/server/README.md
+++ b/server/README.md
@@ -93,6 +93,18 @@ org.apache.rocketmq.studio
云厂商实现放 `provider/<vendor>/` 保持高内聚
- **敏感字段**:VO 上 `@ToString.Exclude`;存储 base64(见 `CredentialUtils`);
列表打码、reveal 接口 admin-only
+- **实例标识:禁用 UUID**。系统不使用 UUID(或任何随机代理键)作为实例标识;
+ 实例 ID(用户输入、人类可读、全局唯一、≤64 字符)是实例的唯一标识,
+ 直接作为 `rmq_instance` 主键(`InstanceService.createInstance` 中 `id = name`),
+ 创建后不可变(更新传入不同名称直接 400 `Instance ID cannot be changed after creation`)。
+ REST 参数(`instanceId`)与关联表外键列(`rmq_topic.instance_id`、`rmq_group.instance_id`、
+ ACL scope、数据源绑定等)一律使用实例 ID;不存在"实例名称"概念,
+ 实例只有**实例 ID** 与 **备注(remark)** 两个文本属性。
+ 解析实例统一走 `InstanceRepository#findByIdentifier`(优先实例 ID,兜底历史主键引用),
+ 不要直接 `findById`;存量 UUID 数据经 `deploy/mysql/upgrade-instance-id-pk.sql` 迁移,
+ 新功能不得新增随机 ID 作为对外标识
+- **测试命名**:Test 方法名以 `Test` 结尾(如 `syncProxyClusterAddsNewInstanceTest`)
+- **checkstyle**:validate 阶段强制,禁止中文字符,Java 代码注释一律用英文
## 构建与测试
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolver.java
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolver.java
index d42bb010..dee110c1 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolver.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolver.java
@@ -29,7 +29,7 @@ public class RuntimeAdminClientResolver {
if (!StringUtils.hasText(instanceId)) {
throw new BusinessException(400, "instanceId is required");
}
- return instanceRepository.findById(instanceId)
+ return instanceRepository.findByIdentifier(instanceId)
.orElseThrow(() -> new BusinessException(404, "Instance not
found: " + instanceId));
}
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceRepository.java
index 546db9b6..a48af8bc 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceRepository.java
@@ -33,6 +33,19 @@ public interface InstanceRepository {
Optional<InstanceVO> findById(String id);
+ Optional<InstanceVO> findByName(String name);
+
+ /**
+ * Resolves an instance by the external instance identifier: matches the
unique name
+ * first, falling back to the internal primary key for legacy references.
+ */
+ default Optional<InstanceVO> findByIdentifier(String identifier) {
+ if (identifier == null || identifier.isBlank()) {
+ return Optional.empty();
+ }
+ return findByName(identifier).or(() -> findById(identifier));
+ }
+
InstanceVO save(InstanceVO instance);
void deleteById(String id);
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
index 29b6f569..163f443b 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
@@ -37,7 +37,6 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
-import java.util.UUID;
@Slf4j
@Service
@@ -96,7 +95,8 @@ public class InstanceService {
case ALIYUN, TENCENT -> createCloudInstance(instance, vendor);
}
- instance.setId(UUID.randomUUID().toString());
+ requireUniqueInstanceName(instance.getName(), null);
+ instance.setId(instance.getName());
instance.setCreatedAt(LocalDateTime.now());
instance.setUpdatedAt(LocalDateTime.now());
InstanceVO saved = instanceRepository.save(instance);
@@ -105,6 +105,17 @@ public class InstanceService {
return saved;
}
+ private void requireUniqueInstanceName(String name, String excludeId) {
+ if (!StringUtils.hasText(name)) {
+ return;
+ }
+ instanceRepository.findByName(name).ifPresent(existing -> {
+ if (excludeId == null || !excludeId.equals(existing.getId())) {
+ throw new BusinessException(400, "Instance name already
exists: " + name);
+ }
+ });
+ }
+
private void createApacheInstance(InstanceVO instance) {
instance.setVendor(InstanceVendor.APACHE);
instance.setName(requireInstanceName(instance.getName()));
@@ -187,7 +198,11 @@ public class InstanceService {
if (!StringUtils.hasText(name)) {
throw new BusinessException(400, "InstanceVO name is required");
}
- return name.trim();
+ String trimmed = name.trim();
+ if (trimmed.length() > 64) {
+ throw new BusinessException(400, "InstanceVO name must not exceed
64 characters");
+ }
+ return trimmed;
}
private String normalizeCredentialRef(String credentialRef) {
@@ -212,7 +227,10 @@ public class InstanceService {
InstanceVO updated = copyOf(existing);
boolean cloudInstance = existing.getVendor() != null &&
existing.getVendor() != InstanceVendor.APACHE;
if (instance.getName() != null) {
- updated.setName(requireInstanceName(instance.getName()));
+ String requestedName = requireInstanceName(instance.getName());
+ if (!requestedName.equals(existing.getName())) {
+ throw new BusinessException(400, "Instance ID cannot be
changed after creation");
+ }
}
if (!cloudInstance) {
if (instance.getType() != null) {
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/MybatisPlusInstanceRepository.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/MybatisPlusInstanceRepository.java
index 4a393e68..961f89fa 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/MybatisPlusInstanceRepository.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/MybatisPlusInstanceRepository.java
@@ -94,6 +94,19 @@ public class MybatisPlusInstanceRepository implements
InstanceRepository {
return Optional.of(toVO(entity));
}
+ @Override
+ public Optional<InstanceVO> findByName(String name) {
+ if (name == null || name.isBlank()) {
+ return Optional.empty();
+ }
+ RmqInstance entity = instanceMapper.selectOne(
+ new QueryWrapper<RmqInstance>().eq("name", name).last("LIMIT
1"));
+ if (entity == null) {
+ return Optional.empty();
+ }
+ return Optional.of(toVO(entity));
+ }
+
@Override
@Transactional
public InstanceVO save(InstanceVO instance) {
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/AclService.java
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/AclService.java
index bece9052..d6ea5435 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/instance/acl/AclService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/instance/acl/AclService.java
@@ -47,7 +47,7 @@ public class AclService {
if (!StringUtils.hasText(instanceId)) {
throw new BusinessException(400, "instanceId is required");
}
- InstanceVO instance = instanceRepository.findById(instanceId)
+ InstanceVO instance = instanceRepository.findByIdentifier(instanceId)
.orElseThrow(() -> new BusinessException(404, "Instance not
found: " + instanceId));
boolean apacheInstance = instance.getVendor() == null ||
instance.getVendor() == InstanceVendor.APACHE;
return new AclCapabilitiesVO(instance.getId(), instance.getVendor(),
instance.getType(),
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistry.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistry.java
index 998a81a6..ebfce9ee 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistry.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistry.java
@@ -58,7 +58,7 @@ public class InstanceProviderRegistry {
if (instanceId == null || instanceId.isBlank()) {
return Optional.empty();
}
- InstanceVO instance = instanceRepository.findById(instanceId)
+ InstanceVO instance = instanceRepository.findByIdentifier(instanceId)
.orElseThrow(() -> new BusinessException(404, "Instance not
found: " + instanceId));
InstanceVendor vendor = instance.getVendor() == null ?
InstanceVendor.APACHE : instance.getVendor();
return Optional.of(forVendor(vendor));
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
index 4e449078..56c38b37 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProvider.java
@@ -77,7 +77,7 @@ public class AliyunInstanceProvider implements
InstanceProvider {
private static final String FIXED_RETRY_POLICY = "FixedRetryPolicy";
private static final int DEFAULT_MAX_RETRY_TIMES = 16;
private static final int DEFAULT_FIXED_RETRY_INTERVAL_SECONDS = 10;
- private static final int COUNT_PAGE_SIZE = 1;
+ private static final int COUNT_PAGE_SIZE = 10;
private static final String RESET_TYPE_SPECIFIED_TIME = "SPECIFIED_TIME";
private static final String RESET_TYPE_LATEST_OFFSET = "LATEST_OFFSET";
@@ -450,7 +450,7 @@ public class AliyunInstanceProvider implements
InstanceProvider {
if (!StringUtils.hasText(instanceId)) {
throw new BusinessException(400, "instanceId is required");
}
- InstanceVO instance = instanceRepository.findById(instanceId)
+ InstanceVO instance = instanceRepository.findByIdentifier(instanceId)
.orElseThrow(() -> new BusinessException(404, "Instance not
found: " + instanceId));
if (!StringUtils.hasText(instance.getCloudInstanceId()) ||
!StringUtils.hasText(instance.getRegionId())
|| !StringUtils.hasText(instance.getCredentialId())) {
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
index aba0521f..f205d5bc 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProvider.java
@@ -752,7 +752,7 @@ public class TencentInstanceProvider implements
InstanceProvider {
if (!StringUtils.hasText(instanceId)) {
throw new BusinessException(400, "instanceId is required");
}
- InstanceVO instance = instanceRepository.findById(instanceId)
+ InstanceVO instance = instanceRepository.findByIdentifier(instanceId)
.orElseThrow(() -> new BusinessException(404, "Instance not
found: " + instanceId));
if (!StringUtils.hasText(instance.getCloudInstanceId()) ||
!StringUtils.hasText(instance.getRegionId())
|| !StringUtils.hasText(instance.getCredentialId())) {
diff --git a/server/src/main/resources/db/schema.sql
b/server/src/main/resources/db/schema.sql
index 34d2ccf6..0841633a 100644
--- a/server/src/main/resources/db/schema.sql
+++ b/server/src/main/resources/db/schema.sql
@@ -30,7 +30,8 @@ CREATE TABLE IF NOT EXISTS rmq_instance (
admin_credential_ref VARCHAR(128) COMMENT 'External Apache admin credential
reference; no secret material',
region_id VARCHAR(128),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ UNIQUE KEY uk_instance_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. Topic 管理记录(通过 Studio 创建/管理的 Topic 元数据)
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolverTest.java
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolverTest.java
index 3ce4df4a..c410806f 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolverTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/cluster/broker/RuntimeAdminClientResolverTest.java
@@ -54,7 +54,7 @@ class RuntimeAdminClientResolverTest {
void resolvesTrimmedEndpointFromSelectedInstance() {
InstanceVO instance = InstanceVO.builder().endpoint(" namesrv-a:9876
").build();
instance.setId("instance-a");
-
when(instanceRepository.findById("instance-a")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("instance-a")).thenReturn(Optional.of(instance));
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
new MqAdminProperties());
@@ -66,9 +66,9 @@ class RuntimeAdminClientResolverTest {
void rejectsUnknownOrUnconfiguredInstances() {
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
new MqAdminProperties());
-
when(instanceRepository.findById("missing")).thenReturn(Optional.empty());
+
when(instanceRepository.findByIdentifier("missing")).thenReturn(Optional.empty());
InstanceVO noEndpoint = InstanceVO.builder().endpoint(" ").build();
-
when(instanceRepository.findById("no-endpoint")).thenReturn(Optional.of(noEndpoint));
+
when(instanceRepository.findByIdentifier("no-endpoint")).thenReturn(Optional.of(noEndpoint));
assertThatThrownBy(() -> resolver.resolveEndpoint("missing"))
.isInstanceOf(BusinessException.class)
@@ -81,7 +81,7 @@ class RuntimeAdminClientResolverTest {
@Test
void executesAgainstTheSelectedInstanceEndpoint() {
InstanceVO instance =
InstanceVO.builder().endpoint("namesrv-b:9876").build();
-
when(instanceRepository.findById("instance-b")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("instance-b")).thenReturn(Optional.of(instance));
when(adminFactory.execute(eq("namesrv-b:9876"), isNull(), isNull(),
any())).thenReturn("done");
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
new MqAdminProperties());
@@ -98,7 +98,7 @@ class RuntimeAdminClientResolverTest {
.endpoint("cloud-endpoint:9876")
.build();
instance.setId("cloud-instance");
-
when(instanceRepository.findById("cloud-instance")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("cloud-instance")).thenReturn(Optional.of(instance));
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
new MqAdminProperties());
@@ -123,7 +123,7 @@ class RuntimeAdminClientResolverTest {
credential.setAccessKey("admin-ak");
credential.setSecretKey("admin-sk");
properties.getCredentials().put("production-admin", credential);
-
when(instanceRepository.findById("instance-b")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("instance-b")).thenReturn(Optional.of(instance));
when(adminFactory.execute(eq("namesrv-b:9876"), any(),
eq("production-admin"), any()))
.thenReturn("done");
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
@@ -154,7 +154,7 @@ class RuntimeAdminClientResolverTest {
InstanceVO instance = InstanceVO.builder().endpoint("namesrv-b:9876")
.adminCredentialRef("missing").build();
instance.setId("instance-b");
-
when(instanceRepository.findById("instance-b")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("instance-b")).thenReturn(Optional.of(instance));
RuntimeAdminClientResolver resolver = new
RuntimeAdminClientResolver(instanceRepository, adminFactory,
properties);
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
index f0e6bb44..b29a8bd4 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
@@ -261,7 +261,7 @@ class InstanceServiceTest {
InstanceVO result = instanceService.createInstance(input);
- assertThat(result.getId()).isNotBlank();
+ assertThat(result.getId()).isEqualTo("new-instance");
assertThat(result.getCreatedAt()).isNotNull();
assertThat(result.getUpdatedAt()).isNotNull();
assertThat(result.getName()).isEqualTo("new-instance");
@@ -391,7 +391,7 @@ class InstanceServiceTest {
existing.setUpdatedAt(originalUpdatedAt);
InstanceVO update = InstanceVO.builder()
- .name("new-name")
+ .name("old-name")
.remark("new remark")
.build();
update.setId("inst-1");
@@ -401,7 +401,7 @@ class InstanceServiceTest {
InstanceVO result = instanceService.updateInstance(update);
- assertThat(result.getName()).isEqualTo("new-name");
+ assertThat(result.getName()).isEqualTo("old-name");
assertThat(result.getEndpoint()).isEqualTo("10.0.1.1:8080");
assertThat(result.getType()).isEqualTo(InstanceType.PROXY);
assertThat(result.getRemark()).isEqualTo("new remark");
@@ -415,19 +415,21 @@ class InstanceServiceTest {
assertThat(existing.getRemark()).isEqualTo("old remark");
assertThat(existing.getUpdatedAt()).isEqualTo(originalUpdatedAt);
verify(operationAuditService).record(eq("UPDATE_INSTANCE"),
eq("INSTANCE"), eq("inst-1"), eq(null),
- eq("name=new-name, vendor=APACHE, type=PROXY"), eq("SUCCESS"),
eq(null));
+ eq("name=old-name, vendor=APACHE, type=PROXY"), eq("SUCCESS"),
eq(null));
}
@Test
- void updateInstanceShouldTrimNameBeforeSaving() {
+ void updateInstanceShouldRejectInstanceIdChangeTest() {
InstanceVO existing =
InstanceVO.builder().name("old-name").endpoint("namesrv:9876").build();
- existing.setId("inst-1");
- InstanceVO update = InstanceVO.builder().name(" production
").build();
- update.setId("inst-1");
-
when(instanceRepository.findById("inst-1")).thenReturn(Optional.of(existing));
-
when(instanceRepository.save(any(InstanceVO.class))).thenAnswer(invocation ->
invocation.getArgument(0));
+ existing.setId("old-name");
+ InstanceVO update = InstanceVO.builder().name("new-name").build();
+ update.setId("old-name");
+
when(instanceRepository.findById("old-name")).thenReturn(Optional.of(existing));
-
assertThat(instanceService.updateInstance(update).getName()).isEqualTo("production");
+ assertThatThrownBy(() -> instanceService.updateInstance(update))
+ .isInstanceOf(BusinessException.class)
+ .hasMessage("Instance ID cannot be changed after creation");
+ verify(instanceRepository, never()).save(any(InstanceVO.class));
}
@Test
@@ -447,7 +449,7 @@ class InstanceServiceTest {
stored.setUpdatedAt(originalUpdatedAt);
InstanceVO update = InstanceVO.builder()
- .name("new-name")
+ .name("old-name")
.remark("new remark")
.type(InstanceType.DIRECT)
.endpoint("10.0.2.2:10911")
@@ -610,7 +612,7 @@ class InstanceServiceTest {
.build();
existing.setId("inst-1");
InstanceVO update = InstanceVO.builder()
- .name("new-name")
+ .name("existing-name")
.endpoint(" ")
.build();
update.setId("inst-1");
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/AclServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/AclServiceTest.java
index ec0ed5f5..4408bcca 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/instance/acl/AclServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/instance/acl/AclServiceTest.java
@@ -104,7 +104,7 @@ class AclServiceTest {
.type(InstanceType.DIRECT)
.build();
instance.setId("instance-1");
-
when(instanceRepository.findById("instance-1")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("instance-1")).thenReturn(Optional.of(instance));
AclCapabilitiesVO capabilities = aclService.capabilities("instance-1");
@@ -118,7 +118,7 @@ class AclServiceTest {
@Test
void capabilitiesShouldRejectUnknownInstance() {
-
when(instanceRepository.findById("missing")).thenReturn(Optional.empty());
+
when(instanceRepository.findByIdentifier("missing")).thenReturn(Optional.empty());
assertThatThrownBy(() -> aclService.capabilities("missing"))
.isInstanceOf(BusinessException.class)
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistryTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistryTest.java
index 633c5f3e..84433c9b 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistryTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/InstanceProviderRegistryTest.java
@@ -71,7 +71,7 @@ class InstanceProviderRegistryTest {
@Test
void byInstanceIdShouldThrowWhenInstanceMissingTest() {
-
when(instanceRepository.findById("missing")).thenReturn(Optional.empty());
+
when(instanceRepository.findByIdentifier("missing")).thenReturn(Optional.empty());
assertThatThrownBy(() -> registry.byInstanceId("missing"))
.isInstanceOf(BusinessException.class)
@@ -81,7 +81,7 @@ class InstanceProviderRegistryTest {
@Test
void byInstanceIdShouldResolveVendorProviderTest() {
InstanceVO instance =
InstanceVO.builder().vendor(InstanceVendor.ALIYUN).build();
-
when(instanceRepository.findById("inst-1")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("inst-1")).thenReturn(Optional.of(instance));
assertThat(registry.byInstanceId("inst-1")).containsSame(aliyunProvider);
}
@@ -89,7 +89,7 @@ class InstanceProviderRegistryTest {
@Test
void byInstanceIdShouldDefaultToApacheWhenVendorNullTest() {
InstanceVO instance = InstanceVO.builder().build();
-
when(instanceRepository.findById("inst-2")).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier("inst-2")).thenReturn(Optional.of(instance));
assertThat(registry.byInstanceId("inst-2")).containsSame(apacheProvider);
}
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
index 17c4f72f..a3d461f4 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/alibaba/AliyunInstanceProviderTest.java
@@ -380,7 +380,7 @@ class AliyunInstanceProviderTest {
.cloudInstanceId(CLOUD_INSTANCE_ID)
.regionId(REGION)
.build();
-
when(instanceRepository.findById(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(instance));
assertThatThrownBy(() -> provider.listTopics(STUDIO_INSTANCE_ID, null,
null))
.isInstanceOf(BusinessException.class)
@@ -396,7 +396,7 @@ class AliyunInstanceProviderTest {
.regionId(REGION)
.credentialId(CREDENTIAL_ID)
.build();
-
when(instanceRepository.findById(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(instance));
+
when(instanceRepository.findByIdentifier(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(instance));
}
private void stubCallThrough() {
@@ -438,7 +438,7 @@ class AliyunInstanceProviderTest {
.data(ListTopicsResponseBody.Data.builder()
.list(List.of(topicRow("topic-a", "NORMAL")))
.pageNumber(1L)
- .pageSize(1L)
+ .pageSize(10L)
.totalCount(321L)
.build())
.build())
@@ -450,7 +450,7 @@ class AliyunInstanceProviderTest {
ArgumentCaptor<ListTopicsRequest> captor =
ArgumentCaptor.forClass(ListTopicsRequest.class);
verify(asyncClient).listTopics(captor.capture());
assertThat(captor.getValue().getPageNumber()).isEqualTo(1L);
- assertThat(captor.getValue().getPageSize()).isEqualTo(1L);
+ assertThat(captor.getValue().getPageSize()).isEqualTo(10L);
}
@Test
@@ -465,7 +465,7 @@ class AliyunInstanceProviderTest {
.consumerGroupId("GID_one")
.build()))
.pageNumber(1L)
- .pageSize(1L)
+ .pageSize(10L)
.totalCount(654L)
.build())
.build())
@@ -478,7 +478,7 @@ class AliyunInstanceProviderTest {
ArgumentCaptor.forClass(ListConsumerGroupsRequest.class);
verify(asyncClient).listConsumerGroups(captor.capture());
assertThat(captor.getValue().getPageNumber()).isEqualTo(1L);
- assertThat(captor.getValue().getPageSize()).isEqualTo(1L);
+ assertThat(captor.getValue().getPageSize()).isEqualTo(10L);
}
@Test
@@ -491,7 +491,7 @@ class AliyunInstanceProviderTest {
.data(ListTopicsResponseBody.Data.builder()
.list(List.of(topicRow("topic-a", "NORMAL")))
.pageNumber(1L)
- .pageSize(1L)
+ .pageSize(10L)
.build())
.build())
.build();
@@ -505,7 +505,7 @@ class AliyunInstanceProviderTest {
ArgumentCaptor<ListTopicsRequest> captor =
ArgumentCaptor.forClass(ListTopicsRequest.class);
verify(asyncClient, times(2)).listTopics(captor.capture());
assertThat(captor.getAllValues()).extracting(ListTopicsRequest::getPageSize)
- .containsExactly(1, AliyunConverters.PAGE_SIZE);
+ .containsExactly(10, AliyunConverters.PAGE_SIZE);
}
@Test
@@ -523,7 +523,7 @@ class AliyunInstanceProviderTest {
ArgumentCaptor.forClass(ListConsumerGroupsRequest.class);
verify(asyncClient, times(2)).listConsumerGroups(captor.capture());
assertThat(captor.getAllValues()).extracting(ListConsumerGroupsRequest::getPageSize)
- .containsExactly(1, AliyunConverters.PAGE_SIZE);
+ .containsExactly(10, AliyunConverters.PAGE_SIZE);
}
private static ListConsumerGroupsResponse groupsResponse(Long totalCount,
String... groupIds) {
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
index 8dd8fb3b..5fed4b7d 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentInstanceProviderTest.java
@@ -97,7 +97,7 @@ class TencentInstanceProviderTest {
@BeforeEach
void setUp() {
provider = new TencentInstanceProvider(clientFactory,
instanceRepository);
-
when(instanceRepository.findById(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(InstanceVO.builder()
+
when(instanceRepository.findByIdentifier(STUDIO_INSTANCE_ID)).thenReturn(Optional.of(InstanceVO.builder()
.name("tencent-prod")
.vendor(InstanceVendor.TENCENT)
.cloudInstanceId(CLOUD_INSTANCE_ID)