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()