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[] = [];