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