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 963e39e8b fix(alert): validate Prometheus duration unit order (#6451)
963e39e8b is described below

commit 963e39e8ba1d983cd140b81a0f4c4a0e64ef5c94
Author: Loyal-Young <[email protected]>
AuthorDate: Thu Oct 15 18:11:28 2026 +0800

    fix(alert): validate Prometheus duration unit order (#6451)
---
 .../studio/ops/alert/AlertRuleRequestDTO.java      |  4 +++-
 .../studio/ops/alert/AlertRuleRequestDTOTest.java  | 24 ++++++++++++++++++++++
 .../studio/ops/alert/AlertServiceTest.java         | 20 ++++++++++++++++++
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTO.java
 
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTO.java
index fb82f66f8..c052372c1 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTO.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTO.java
@@ -26,7 +26,9 @@ import java.util.List;
 
 @Data
 public class AlertRuleRequestDTO {
-    static final String PROMETHEUS_DURATION_REGEXP = 
"(?:[0-9]+(?:ms|s|m|h|d|w|y))+";
+    // Prometheus requires each unit at most once, ordered from largest to 
smallest.
+    static final String PROMETHEUS_DURATION_REGEXP = 
"(?=.+)(?:[0-9]+y)?(?:[0-9]+w)?(?:[0-9]+d)?"
+            + "(?:[0-9]+h)?(?:[0-9]+m)?(?:[0-9]+s)?(?:[0-9]+ms)?";
 
     private Long id;
     @NotBlank(message = "name is required")
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTOTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTOTest.java
index 27b3be64d..cebe272a2 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTOTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertRuleRequestDTOTest.java
@@ -19,6 +19,8 @@ package org.apache.rocketmq.studio.ops.alert;
 import jakarta.validation.Validation;
 import jakarta.validation.Validator;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.Arrays;
 import java.util.List;
@@ -29,6 +31,28 @@ class AlertRuleRequestDTOTest {
 
     private final Validator validator = 
Validation.buildDefaultValidatorFactory().getValidator();
 
+    @ParameterizedTest
+    @ValueSource(strings = {"1s1m", "1m1m", "1ms1s", "1h2d", "1y1y", "", 
"1.5m"})
+    void durationShouldRejectUnorderedOrRepeatedUnitsTest(String duration) {
+        AlertRuleRequestDTO request = new AlertRuleRequestDTO();
+        request.setName("High Lag");
+        request.setDuration(duration);
+
+        assertThat(validator.validate(request))
+                .extracting(violation -> 
violation.getPropertyPath().toString())
+                .containsExactly("duration");
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"1y2w3d4h5m6s7ms", "1h30m", "2w3d", "500ms", "0s", 
"1m1ms"})
+    void durationShouldAcceptDescendingUnitsTest(String duration) {
+        AlertRuleRequestDTO request = new AlertRuleRequestDTO();
+        request.setName("High Lag");
+        request.setDuration(duration);
+
+        assertThat(validator.validate(request)).isEmpty();
+    }
+
     @Test
     void channelsShouldRejectNullBlankAndUnsupportedElementsTest() {
         AlertRuleRequestDTO request = new AlertRuleRequestDTO();
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
index 3c4621a62..5bcc10b4f 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/ops/alert/AlertServiceTest.java
@@ -303,6 +303,26 @@ class AlertServiceTest {
         assertThat(result).contains("for: 1h30m").doesNotContain("for: 5m");
     }
 
+    @Test
+    void exportPrometheusRulesYamlShouldReplaceUnorderedLegacyDurationsTest() {
+        for (String duration : List.of("1s1m", "1m1m", "1ms1s", "1h2d")) {
+            AlertRuleVO rule = AlertRuleVO.builder()
+                    .name("High Lag")
+                    .metric("rocketmq_consumer_lag_messages")
+                    .operator(">")
+                    .threshold(5000)
+                    .duration(duration)
+                    .enabled(true)
+                    .build();
+            when(alertRepository.findAllRules()).thenReturn(List.of(rule));
+
+            assertThat(alertService.exportPrometheusRulesYaml())
+                    .as("legacy duration %s", duration)
+                    .contains("for: 5m")
+                    .doesNotContain("for: " + duration);
+        }
+    }
+
     @Test
     void exportPrometheusRulesYamlShouldExcludeNativeClusterRulesTest() {
         AlertRuleVO legacy = AlertRuleVO.builder()

Reply via email to