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 51fc7eaf fix: batch of small backend validation and export fixes 
(#1103, #1105, #1233, #1241, #1242)
51fc7eaf is described below

commit 51fc7eafcef3a94eeabde24c39b470a2d85b5da4
Author: lizhimins <[email protected]>
AuthorDate: Mon Aug 10 11:52:37 2026 +0800

    fix: batch of small backend validation and export fixes (#1103, #1105, 
#1233, #1241, #1242)
    
    * fix(lite-topic): return 400 for invalid extendTTL input
    
    * fix(grafana): use attachment disposition type for dashboard export
    
    * fix(metrics): pass through upstream 504 and guard null result elements
    
    * fix(topic): require newTTL on TTL update
    
    * fix(metrics): validate nested query in datasource queries
    
    ---------
    
    Co-authored-by: yyqdbngt <[email protected]>
---
 .../metrics/AbstractPrometheusCompatibleMetricsSource.java   | 12 ++++++++++--
 .../cluster/metrics/grafana/GrafanaDashboardController.java  |  4 +++-
 .../rocketmq/studio/instance/topic/LiteTopicService.java     |  4 ++--
 .../studio/instance/topic/LiteTopicTTLUpdateDTO.java         |  2 ++
 .../studio/model/request/MetricsDataSourceQueryRequest.java  |  4 ++++
 .../metrics/grafana/GrafanaDashboardControllerTest.java      |  2 +-
 .../rocketmq/studio/instance/topic/LiteTopicServiceTest.java | 10 ++++++----
 7 files changed, 28 insertions(+), 10 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/AbstractPrometheusCompatibleMetricsSource.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/AbstractPrometheusCompatibleMetricsSource.java
index 3af76c5a..a0ce125d 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/AbstractPrometheusCompatibleMetricsSource.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/AbstractPrometheusCompatibleMetricsSource.java
@@ -211,6 +211,10 @@ public abstract class 
AbstractPrometheusCompatibleMetricsSource implements Metri
     }
 
     private MetricDataVO.MetricSeriesVO parseSeries(JsonNode seriesNode) {
+        if (seriesNode == null) {
+            throw new PrometheusException(HttpStatus.BAD_GATEWAY.value(),
+                    backendLabel() + " returned a malformed time series");
+        }
         JsonNode metric = seriesNode.path("metric");
         JsonNode values = seriesNode.path("values");
         JsonNode histograms = seriesNode.path("histograms");
@@ -254,7 +258,8 @@ public abstract class 
AbstractPrometheusCompatibleMetricsSource implements Metri
     }
 
     private MetricDataVO.MetricSampleVO parseSample(JsonNode sampleNode) {
-        if (!sampleNode.isArray() || sampleNode.size() != 2 || 
!sampleNode.get(0).isNumber()) {
+        if (sampleNode == null || !sampleNode.isArray() || sampleNode.size() 
!= 2
+                || !sampleNode.get(0).isNumber()) {
             throw new PrometheusException(HttpStatus.BAD_GATEWAY.value(),
                     backendLabel() + " returned a malformed sample");
         }
@@ -307,6 +312,9 @@ public abstract class 
AbstractPrometheusCompatibleMetricsSource implements Metri
         }
         long totalSamples = 0;
         for (JsonNode series : result) {
+            if (series == null) {
+                continue;
+            }
             totalSamples += series.path("values").size();
             totalSamples += series.path("histograms").size();
             if (totalSamples > MAX_TOTAL_SAMPLES) {
@@ -319,7 +327,7 @@ public abstract class 
AbstractPrometheusCompatibleMetricsSource implements Metri
     private int responseStatus(HttpStatusCode statusCode) {
         int upstreamStatus = statusCode.value();
         return switch (upstreamStatus) {
-            case 400, 422, 503 -> upstreamStatus;
+            case 400, 422, 503, 504 -> upstreamStatus;
             default -> HttpStatus.BAD_GATEWAY.value();
         };
     }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardController.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardController.java
index fd8c9197..c288e86b 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardController.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardController.java
@@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import lombok.RequiredArgsConstructor;
 import org.apache.rocketmq.studio.common.domain.Result;
+import org.springframework.http.ContentDisposition;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
@@ -66,7 +67,8 @@ public class GrafanaDashboardController {
         String json = grafanaDashboardService.getDashboardJson(uid);
         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_JSON);
-        headers.setContentDispositionFormData("attachment", uid + ".json");
+        headers.setContentDisposition(
+                ContentDisposition.attachment().filename(uid + 
".json").build());
         return new ResponseEntity<>(json.getBytes(StandardCharsets.UTF_8), 
headers, HttpStatus.OK);
     }
 }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicService.java
index 39040ae6..6d70fb4c 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicService.java
@@ -37,10 +37,10 @@ public class LiteTopicService {
 
     public void extendTTL(String topicPattern, Long newTTL) {
         if (topicPattern == null || topicPattern.isBlank()) {
-            throw new IllegalArgumentException("topicPattern is required");
+            throw new BusinessException(400, "topicPattern is required");
         }
         if (newTTL == null || newTTL <= 0) {
-            throw new IllegalArgumentException("newTTL must be positive");
+            throw new BusinessException(400, "newTTL must be positive");
         }
         throw new BusinessException(NOT_IMPLEMENTED, 
PROVIDER_UNAVAILABLE_MESSAGE);
     }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicTTLUpdateDTO.java
 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicTTLUpdateDTO.java
index 206948f4..2e37c68e 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicTTLUpdateDTO.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/topic/LiteTopicTTLUpdateDTO.java
@@ -18,6 +18,7 @@
 package org.apache.rocketmq.studio.instance.topic;
 
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Positive;
 import lombok.Data;
 
@@ -25,6 +26,7 @@ import lombok.Data;
 public class LiteTopicTTLUpdateDTO {
     @NotBlank(message = "topicPattern is required")
     private String topicPattern;
+    @NotNull(message = "newTTL is required")
     @Positive(message = "newTTL must be positive")
     private Long newTTL;
 }
diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceQueryRequest.java
 
b/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceQueryRequest.java
index 5c4468a0..d910af80 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceQueryRequest.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/model/request/MetricsDataSourceQueryRequest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.rocketmq.studio.model.request;
 
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
 import org.apache.rocketmq.studio.cluster.metrics.MetricQueryDTO;
 import lombok.Data;
 
@@ -30,6 +32,8 @@ import lombok.Data;
 @Data
 public class MetricsDataSourceQueryRequest {
 
+    @Valid
+    @NotNull(message = "query is required")
     private MetricQueryDTO query;
 
     private String username;
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardControllerTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardControllerTest.java
index 09e828b8..e97c4781 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardControllerTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/metrics/grafana/GrafanaDashboardControllerTest.java
@@ -81,7 +81,7 @@ class GrafanaDashboardControllerTest {
                 .andExpect(status().isOk())
                 .andExpect(header().string("Content-Type", "application/json"))
                 .andExpect(header().string("Content-Disposition",
-                        "form-data; name=\"attachment\"; 
filename=\"rocketmq-overview.json\""))
+                        "attachment; filename=\"rocketmq-overview.json\""))
                 .andExpect(content().json("{\"uid\":\"rocketmq-overview\"}"));
     }
 }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/topic/LiteTopicServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/topic/LiteTopicServiceTest.java
index 1c921338..e1d6baf8 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/topic/LiteTopicServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/topic/LiteTopicServiceTest.java
@@ -57,11 +57,13 @@ class LiteTopicServiceTest {
     @Test
     void extendTTLShouldRejectInvalidInput() {
         assertThatThrownBy(() -> liteTopicService.extendTTL("", 1L))
-                .isInstanceOf(IllegalArgumentException.class)
-                .hasMessage("topicPattern is required");
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("topicPattern is required")
+                .satisfies(ex -> assertThat(((BusinessException) 
ex).getCode()).isEqualTo(400));
         assertThatThrownBy(() -> 
liteTopicService.extendTTL("chat/{sessionId}", 0L))
-                .isInstanceOf(IllegalArgumentException.class)
-                .hasMessage("newTTL must be positive");
+                .isInstanceOf(BusinessException.class)
+                .hasMessage("newTTL must be positive")
+                .satisfies(ex -> assertThat(((BusinessException) 
ex).getCode()).isEqualTo(400));
     }
 
     @Test

Reply via email to