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 0d3b3fd33 fix(alerts): make template previews match delivery 
substitutions (#6664)
0d3b3fd33 is described below

commit 0d3b3fd3339d354a7b834b27b6e10be9b1b30efc
Author: 健康宝 <[email protected]>
AuthorDate: Thu Oct 15 19:07:55 2026 +0800

    fix(alerts): make template previews match delivery substitutions (#6664)
---
 web/src/pages/ops/alerts.tsx               |  9 +++++++--
 web/src/utils/alertTemplatePreview.test.ts | 14 +++++++++++++-
 web/src/utils/alertTemplatePreview.ts      | 13 +++++++++++--
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/web/src/pages/ops/alerts.tsx b/web/src/pages/ops/alerts.tsx
index 33fbcc9e0..99ee3ee59 100644
--- a/web/src/pages/ops/alerts.tsx
+++ b/web/src/pages/ops/alerts.tsx
@@ -150,7 +150,10 @@ export const formatThresholdCondition = (
 const previewValueForMetric = (metric?: string, threshold?: string | number | 
null): string => {
   if (metric && nativeRatioMetrics.has(metric)) {
     const value = Number(threshold);
-    return Number.isFinite(value) ? `${Math.min(100, value + 6)}%` : '91%';
+    // The server renders ${value} without a unit (the percent sign lives in
+    // ${thresholdUnit}); embedding it here produced "91%%" in previews of
+    // templates like "${value}${thresholdUnit}".
+    return Number.isFinite(value) ? `${Math.min(100, value + 6)}` : '91';
   }
   if (metric === 'consumer.delay.seconds') return '420';
   if (metric === 'consumer.lag.total' || metric === 'topic.backlog.total') 
return '12000';
@@ -294,7 +297,9 @@ const AlertsPageContent = ({ domain = 'CLUSTER' }: 
AlertsPageProps) => {
             ? previewDescription.trim()
             : t('alerts.notificationTemplateSampleDescription'),
         transition: 'FIRING',
-        metric: metricText,
+        // The server substitutes the raw metric key; a translated label here
+        // made the preview disagree with what delivery actually sends.
+        metric: metricKey,
         instanceId,
         value: previewValueForMetric(metricKey, threshold),
         threshold,
diff --git a/web/src/utils/alertTemplatePreview.test.ts 
b/web/src/utils/alertTemplatePreview.test.ts
index fa19aaa3f..a1ce48cb2 100644
--- a/web/src/utils/alertTemplatePreview.test.ts
+++ b/web/src/utils/alertTemplatePreview.test.ts
@@ -116,7 +116,13 @@ describe('alert template preview', () => {
     const preview = previewAlertNotificationTemplate('   ');
 
     expect(preview.status).toBe('ready');
-    expect(preview.rendered).toBe('   ');
+    // An empty configuration still delivers the server-side default, so the
+    // preview renders that content instead of an empty string.
+    expect(preview.rendered).toBe(
+      '[CRITICAL] Broker disk usage is firing'
+        + ' - Broker broker-a disk usage is above the configured threshold.'
+        + '\nLabels: broker=broker-a, cluster=DefaultCluster, 
instanceId=rocketmq-prod',
+    );
     expect(preview.issues).toEqual([
       expect.objectContaining({
         code: 'EMPTY_TEMPLATE',
@@ -125,6 +131,12 @@ describe('alert template preview', () => {
     ]);
   });
 
+  it('keeps ${value} unit-free so value+thresholdUnit previews match 
delivery', () => {
+    const preview = 
previewAlertNotificationTemplate('${value}${thresholdUnit}');
+
+    expect(preview.rendered).toBe('91%');
+  });
+
   it('formats label maps in deterministic key order', () => {
     const context = buildAlertTemplatePreviewContext({
       labels: {
diff --git a/web/src/utils/alertTemplatePreview.ts 
b/web/src/utils/alertTemplatePreview.ts
index 27a2b63a9..36a28b60e 100644
--- a/web/src/utils/alertTemplatePreview.ts
+++ b/web/src/utils/alertTemplatePreview.ts
@@ -93,7 +93,7 @@ const defaultContext: Required<AlertTemplatePreviewContext> = 
{
   transition: 'FIRING',
   metric: 'broker.disk.usage_ratio',
   instanceId: 'rocketmq-prod',
-  value: '91%',
+  value: '91',
   threshold: '85',
   thresholdUnit: '%',
   level: 'CRITICAL',
@@ -108,6 +108,12 @@ const defaultContext: 
Required<AlertTemplatePreviewContext> = {
 const uniqueSorted = (values: string[]) =>
   Array.from(new Set(values.filter(Boolean))).sort((left, right) => 
left.localeCompare(right));
 
+// Mirrors the server-side fallback 
(AlertNotificationTemplate.DEFAULT_TEMPLATE):
+// an empty custom configuration still delivers this content, so the preview
+// must show it instead of an empty string.
+export const SERVER_DEFAULT_NOTIFICATION_TEMPLATE =
+  '[${level}] ${title} - ${description}\nLabels: ${labels}';
+
 const normalizeTemplate = (template?: string | null) => template ?? '';
 
 const isEmptyValue = (value: unknown) =>
@@ -248,7 +254,10 @@ export function previewAlertNotificationTemplate(
   const template = normalizeTemplate(templateInput);
   const maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;
   const values = buildAlertTemplatePreviewContext(context);
-  const parsed = parseTemplate(template.trim() ? template.trim() : template, 
values);
+  const parsed = parseTemplate(
+    template.trim() ? template.trim() : SERVER_DEFAULT_NOTIFICATION_TEMPLATE,
+    values,
+  );
   const unusedContextVariables = 
collectUnusedContextVariables(parsed.usedVariables, values);
   const issues: AlertTemplatePreviewIssue[] = [];
 

Reply via email to