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 d24f58b82 fix(tencent): reject incomplete catalog pages (#4529)
d24f58b82 is described below
commit d24f58b82667c22277720c7afe2e5cbf69c089d5
Author: zmuxuny <[email protected]>
AuthorDate: Mon Sep 21 17:30:00 2026 +0800
fix(tencent): reject incomplete catalog pages (#4529)
TencentCatalogService.listCloudInstances treated a null/empty or short page
as the end of the catalog without consulting TotalCount, so a truncated page
was reported as a complete instance list. A page that contradicts a known
non-negative TotalCount now raises BusinessException(502); genuinely empty
catalogs and unknown totals keep the existing behaviour.
Fixes #4528
---
.../provider/tencent/TencentCatalogService.java | 23 +++++++++---
.../tencent/TencentCatalogServiceTest.java | 42 ++++++++++++++++++++++
2 files changed, 61 insertions(+), 4 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
index 744980ed7..f06be61e2 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogService.java
@@ -89,7 +89,13 @@ public class TencentCatalogService implements
CloudCatalogProvider {
DescribeInstanceListResponse response =
clientFactory.call(credentialId, regionId,
client -> client.DescribeInstanceList(request));
InstanceItem[] data = response == null ? null : response.getData();
- if (data == null || data.length == 0) {
+ Long totalCount = response == null ? null :
response.getTotalCount();
+ int returned = data == null ? 0 : data.length;
+ if (isIncompletePage(offset, returned, totalCount)) {
+ throw new BusinessException(502,
+ "Tencent Cloud RocketMQ instance catalog returned an
incomplete page");
+ }
+ if (returned == 0) {
break;
}
for (InstanceItem item : data) {
@@ -101,15 +107,24 @@ public class TencentCatalogService implements
CloudCatalogProvider {
instances.add(option);
}
}
- if (data.length < PAGE_SIZE || hasFetchedAll(offset,
response.getTotalCount())) {
+ if (hasFetchedAll(offset, returned, totalCount)
+ || isUnknownTotalCount(totalCount) && returned <
PAGE_SIZE) {
break;
}
}
return instances;
}
- private static boolean hasFetchedAll(long offset, Long totalCount) {
- return totalCount != null && totalCount >= 0L && offset + PAGE_SIZE >=
totalCount;
+ private static boolean isIncompletePage(long offset, int returned, Long
totalCount) {
+ return totalCount != null && totalCount >= 0L && offset + returned <
totalCount && returned < PAGE_SIZE;
+ }
+
+ private static boolean hasFetchedAll(long offset, int returned, Long
totalCount) {
+ return totalCount != null && totalCount >= 0L && offset + returned >=
totalCount;
+ }
+
+ private static boolean isUnknownTotalCount(Long totalCount) {
+ return totalCount == null || totalCount < 0L;
}
@Override
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
index 03d15b621..3ed018119 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/provider/tencent/TencentCatalogServiceTest.java
@@ -22,6 +22,7 @@ import
com.tencentcloudapi.trocket.v20230308.models.DescribeInstanceResponse;
import com.tencentcloudapi.trocket.v20230308.models.Endpoint;
import com.tencentcloudapi.trocket.v20230308.models.InstanceItem;
import com.tencentcloudapi.trocket.v20230308.TrocketClient;
+import org.apache.rocketmq.studio.common.exception.BusinessException;
import org.apache.rocketmq.studio.provider.CloudInstanceDetailVO;
import org.apache.rocketmq.studio.provider.CloudInstanceOptionVO;
import org.apache.rocketmq.studio.provider.CloudRegionVO;
@@ -34,6 +35,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
@@ -133,6 +135,46 @@ class TencentCatalogServiceTest {
.isEqualTo("rmq-valid");
}
+ @Test
+ void
listCloudInstancesShouldRejectIncompleteEmptyPageWhenTotalCountRequiresDataTest()
{
+ DescribeInstanceListResponse response = new
DescribeInstanceListResponse();
+ response.setTotalCount(1L);
+ response.setData(null);
+ when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION),
any())).thenReturn(response);
+
+ assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID,
REGION, null))
+ .isInstanceOf(BusinessException.class)
+ .satisfies(error -> assertThat(
+ ((BusinessException) error).getCode())
+ .isEqualTo(502));
+ }
+
+ @Test
+ void
listCloudInstancesShouldRejectIncompleteShortPageWhenTotalCountRequiresMoreTest()
{
+ InstanceItem item = new InstanceItem();
+ item.setInstanceId("rmq-only-row");
+ DescribeInstanceListResponse response = new
DescribeInstanceListResponse();
+ response.setTotalCount(2L);
+ response.setData(new InstanceItem[]{item});
+ when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION),
any())).thenReturn(response);
+
+ assertThatThrownBy(() -> service.listCloudInstances(CREDENTIAL_ID,
REGION, null))
+ .isInstanceOf(BusinessException.class)
+ .satisfies(error -> assertThat(
+ ((BusinessException) error).getCode())
+ .isEqualTo(502));
+ }
+
+ @Test
+ void listCloudInstancesShouldKeepGenuineEmptyCatalogTest() {
+ DescribeInstanceListResponse response = new
DescribeInstanceListResponse();
+ response.setTotalCount(0L);
+ response.setData(null);
+ when(clientFactory.call(eq(CREDENTIAL_ID), eq(REGION),
any())).thenReturn(response);
+
+ assertThat(service.listCloudInstances(CREDENTIAL_ID, REGION,
null)).isEmpty();
+ }
+
@Test
void
listCloudInstancesShouldContinuePastTenThousandRecordsWhenTotalCountRequiresItTest()
throws Exception {
InstanceItem item = new InstanceItem();