Skip to content

feat: add quick-create notification rule from pasted webhook/integration URL - #2217

Merged
jsers merged 2 commits into
mainfrom
create-notify-rule-0727
Jul 27, 2026
Merged

feat: add quick-create notification rule from pasted webhook/integration URL#2217
jsers merged 2 commits into
mainfrom
create-notify-rule-0727

Conversation

@710leo

@710leo 710leo commented Jul 27, 2026

Copy link
Copy Markdown
Member
image

Summary by CodeRabbit

  • New Features
    • Added quick creation of notification rules by pasting supported webhook or integration URLs.
    • Automatically detects supported channels, suggests rule names, validates input, and reuses matching existing configurations when possible.
    • Added quick-create actions to the notification rule selector, including automatic selection of newly created rules.
    • Disabled notification rules are now visibly marked.
  • Localization
    • Added quick-create interface text and validation messages across supported languages.
  • Tests
    • Added coverage for webhook parsing, validation, provider detection, and rule-name suggestions.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds webhook-based quick creation for notification rules, including provider parsing, channel/rule reuse or provisioning, a modal workflow, selector integration, automatic selection, validation tests, and localized UI text.

Changes

Notification rule quick creation

Layer / File(s) Summary
Webhook parsing and naming
src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts, src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.test.ts
Parses DingTalk, WeCom, Feishu/Lark Card, and FlashDuty URLs, validates required parameters, provides non-throwing results, and generates suggested rule names with coverage for supported formats and failures.
Channel and rule provisioning
src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts, src/pages/notificationRules/components/RuleDropdownSelect/QuickCreateModal.tsx
Reuses or creates provider channels, selects templates, creates notification rules, and submits modal values with permission checks, validation, loading state, and error handling.
Rule selector integration and localization
src/pages/notificationRules/components/RuleDropdownSelect/index.tsx, src/pages/notificationRules/locale/*
Adds quick-create actions and modal wiring, refreshes and selects created rules, displays disabled rules, and defines localized quick-create text in five locales.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RuleDropdownSelect
  participant QuickCreateModal
  participant quickCreateNotifyRule
  participant NotificationServices
  RuleDropdownSelect->>QuickCreateModal: open quick-create
  QuickCreateModal->>quickCreateNotifyRule: submit webhook and rule fields
  quickCreateNotifyRule->>NotificationServices: reuse or create channel and rule
  NotificationServices-->>quickCreateNotifyRule: return creation result
  quickCreateNotifyRule-->>QuickCreateModal: return ruleId and reused
  QuickCreateModal-->>RuleDropdownSelect: refresh and select rule
Loading

Possibly related PRs

  • n9e/fe#2198: Overlaps in the shared rule selector component while refactoring it into a reusable selector.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and accurately summarizes the main change: quick-creating notification rules from pasted webhook or integration URLs.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch create-notify-rule-0727

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts (1)

46-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate "last-4-of-token" suffix logic across two files. quickCreate.ts defines this exact truncation rule in a private tokenSuffix helper, and QuickCreateModal.tsx reimplements it inline for the "detected" hint instead of importing it.

  • src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts#L46-L48: export tokenSuffix (export function tokenSuffix(token: string) { ... }).
  • src/pages/notificationRules/components/RuleDropdownSelect/QuickCreateModal.tsx#L94-L106: import tokenSuffix from ./quickCreate and replace token.length >= 4 ? token.slice(-4) : token with tokenSuffix(token).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts`
around lines 46 - 48, Export the tokenSuffix helper in quickCreate.ts so it can
be reused. In QuickCreateModal.tsx, import tokenSuffix from ./quickCreate and
replace the inline last-four-character truncation in the detected hint with
tokenSuffix(token), preserving the existing behavior at both sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/notificationRules/components/RuleDropdownSelect/index.tsx`:
- Around line 326-331: Replace the toolbar quick-create `<a>` in the authorized
branch with the same `<Button type="link">` pattern used by the empty-state
action, preserving `handleQuickCreate`, the icon, and translated label so the
trigger is keyboard accessible.
- Around line 388-397: Replace the inline ID extraction in the
RuleDropdownSelect success handler with the exported extractId helper from
quickCreate.ts. Reuse that shared helper for the postItems response, then retain
the existing finite-positive check and selectRule call.

In `@src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts`:
- Around line 201-203: Replace the substring-based Flashduty integration lookup
with an exact comparison of the stored integration key against parsed.token in
findRuleByToken and the related quick-create flow. Reuse
flashdutyIntegrationUrlOf only if it returns the exact key, preserving the
existing IM-provider matching behavior and preventing matches caused by URL
suffixes or query parameters.

In
`@src/pages/notificationRules/components/RuleDropdownSelect/QuickCreateModal.tsx`:
- Around line 36-40: Reuse the memoized parseResult from the component’s useMemo
in both suggestQuickRuleName/handleUrlChange and the webhook URL validator
instead of calling tryParseWebhookInput repeatedly; ensure name suggestion and
validation use the current parsed value, accounting for watch-driven update
timing.
- Around line 42-54: Update the visible-state effect in QuickCreateModal to
clear the teams state when the modal is closed, while preserving the existing
form reset and team-loading behavior when visible is true. Ensure the close path
explicitly resets this temporary local state before returning.

---

Nitpick comments:
In `@src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts`:
- Around line 46-48: Export the tokenSuffix helper in quickCreate.ts so it can
be reused. In QuickCreateModal.tsx, import tokenSuffix from ./quickCreate and
replace the inline last-four-character truncation in the detected hint with
tokenSuffix(token), preserving the existing behavior at both sites.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bfdde92c-d11e-4dba-9af5-0ff25473755f

📥 Commits

Reviewing files that changed from the base of the PR and between 205996c and 006188f.

📒 Files selected for processing (9)
  • src/pages/notificationRules/components/RuleDropdownSelect/QuickCreateModal.tsx
  • src/pages/notificationRules/components/RuleDropdownSelect/index.tsx
  • src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.test.ts
  • src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts
  • src/pages/notificationRules/locale/en_US.ts
  • src/pages/notificationRules/locale/ja_JP.ts
  • src/pages/notificationRules/locale/ru_RU.ts
  • src/pages/notificationRules/locale/zh_CN.ts
  • src/pages/notificationRules/locale/zh_HK.ts

Comment on lines +326 to +331
{isAuthorized && (
<a onClick={handleQuickCreate}>
<ThunderboltOutlined className='mr-1' />
{t('rule_select.quick_create.action')}
</a>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Quick-create toolbar trigger uses <a> without href, breaking keyboard access.

The identical action is exposed as <Button type='link'> in the empty-state (lines 277-279), which is keyboard-focusable/activatable. This <a onClick={...}> with no href isn't part of the native tab order and can't be triggered via keyboard (Enter) in most browsers, unlike a real link or a <Button>.

🛠️ Proposed fix: use the same `Button type='link'` pattern used elsewhere in this file
-          {isAuthorized && (
-            <a onClick={handleQuickCreate}>
-              <ThunderboltOutlined className='mr-1' />
-              {t('rule_select.quick_create.action')}
-            </a>
-          )}
+          {isAuthorized && (
+            <Button type='link' size='small' icon={<ThunderboltOutlined />} onClick={handleQuickCreate}>
+              {t('rule_select.quick_create.action')}
+            </Button>
+          )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{isAuthorized && (
<a onClick={handleQuickCreate}>
<ThunderboltOutlined className='mr-1' />
{t('rule_select.quick_create.action')}
</a>
)}
{isAuthorized && (
<Button type='link' size='small' icon={<ThunderboltOutlined />} onClick={handleQuickCreate}>
{t('rule_select.quick_create.action')}
</Button>
)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notificationRules/components/RuleDropdownSelect/index.tsx` around
lines 326 - 331, Replace the toolbar quick-create `<a>` in the authorized branch
with the same `<Button type="link">` pattern used by the empty-state action,
preserving `handleQuickCreate`, the icon, and translated label so the trigger is
keyboard accessible.

Comment on lines +388 to +397
.then((dat) => {
message.success(t('common:success.add'));
handleCloseCreateDrawer();
refresh?.();
// 自动选中新建的规则,免去用户再去下拉里手动勾选
const first = Array.isArray(dat) ? dat[0] : undefined;
const newId = typeof first === 'object' ? Number(first?.id) : Number(first);
if (Number.isFinite(newId) && newId > 0) {
selectRule(newId);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Duplicated, divergent id-extraction logic vs. quickCreate.ts's extractId.

This inline extraction sets first = undefined when dat isn't an array, while quickCreate.ts's extractId (used against the very same postItems API) falls back to first = dat. Both consume the same create-rule API response shape, so any divergence in actual response format silently disables auto-select here but not in quickCreate.ts. Export and reuse extractId instead of re-implementing it.

♻️ Proposed fix
 // quickCreate.ts
-function extractId(dat: any): number | undefined {
+export function extractId(dat: any): number | undefined {
 // index.tsx
+import { extractId } from './quickCreate';
...
               createNotificationRules([values])
                 .then((dat) => {
                   message.success(t('common:success.add'));
                   handleCloseCreateDrawer();
                   refresh?.();
                   // 自动选中新建的规则,免去用户再去下拉里手动勾选
-                  const first = Array.isArray(dat) ? dat[0] : undefined;
-                  const newId = typeof first === 'object' ? Number(first?.id) : Number(first);
-                  if (Number.isFinite(newId) && newId > 0) {
-                    selectRule(newId);
-                  }
+                  const newId = extractId(dat);
+                  if (newId) {
+                    selectRule(newId);
+                  }
                 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.then((dat) => {
message.success(t('common:success.add'));
handleCloseCreateDrawer();
refresh?.();
// 自动选中新建的规则,免去用户再去下拉里手动勾选
const first = Array.isArray(dat) ? dat[0] : undefined;
const newId = typeof first === 'object' ? Number(first?.id) : Number(first);
if (Number.isFinite(newId) && newId > 0) {
selectRule(newId);
}
.then((dat) => {
message.success(t('common:success.add'));
handleCloseCreateDrawer();
refresh?.();
// 自动选中新建的规则,免去用户再去下拉里手动勾选
const newId = extractId(dat);
if (newId) {
selectRule(newId);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notificationRules/components/RuleDropdownSelect/index.tsx` around
lines 388 - 397, Replace the inline ID extraction in the RuleDropdownSelect
success handler with the exported extractId helper from quickCreate.ts. Reuse
that shared helper for the postItems response, then retain the existing
finite-positive check and selectRule call.

Comment on lines +201 to +203
function flashdutyIntegrationUrlOf(channel: any): string {
return String(channel?.request_config?.flashduty_request_config?.integration_url || '').trim();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Flashduty dedup uses substring match on integration_key, risking false-positive reuse.

flashdutyIntegrationUrlOf(c).includes(parsed.token) is a substring check, unlike the exact match used for IM providers in findRuleByToken (String(c) === token). If one integration key happens to be a substring of another channel's stored integration_url (or the URL contains extra query params), this will incorrectly report reused: true against the wrong rule, silently skipping creation of the user's actual intended notification path.

🐛 Proposed fix: exact-match the integration_key instead of substring search
 function flashdutyIntegrationUrlOf(channel: any): string {
   return String(channel?.request_config?.flashduty_request_config?.integration_url || '').trim();
 }
+
+function flashdutyIntegrationKeyOf(channel: any): string {
+  const url = flashdutyIntegrationUrlOf(channel);
+  try {
+    return new URL(url).searchParams.get('integration_key') || '';
+  } catch {
+    return '';
+  }
+}
-        if (isFlashduty && flashdutyIntegrationUrlOf(c).includes(parsed.token) && c?.id != null) {
+        if (isFlashduty && flashdutyIntegrationKeyOf(c) === parsed.token && c?.id != null) {

Also applies to: 284-306

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/notificationRules/components/RuleDropdownSelect/quickCreate.ts`
around lines 201 - 203, Replace the substring-based Flashduty integration lookup
with an exact comparison of the stored integration key against parsed.token in
findRuleByToken and the related quick-create flow. Reuse
flashdutyIntegrationUrlOf only if it returns the exact key, preserving the
existing IM-provider matching behavior and preventing matches caused by URL
suffixes or query parameters.

Comment on lines +36 to +40
const parseResult = useMemo(() => {
const trimmed = _.trim(urlValue || '');
if (!trimmed) return undefined;
return tryParseWebhookInput(trimmed);
}, [urlValue]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Webhook URL is re-parsed independently in three places per keystroke.

parseResult (36-40) already recomputes tryParseWebhookInput reactively via Form.useWatch, yet handleUrlChange (via suggestQuickRuleName, line 57) and the field validator (135-137) each call tryParseWebhookInput again on the same value. Reuse the memoized parseResult instead of recomputing.

♻️ Proposed fix: reuse `parseResult` in the validator and name suggestion
   const handleUrlChange = (value: string) => {
-    const suggestion = suggestQuickRuleName(value);
-    if (!suggestion) return;
+    const trimmed = _.trim(value || '');
+    const result = tryParseWebhookInput(trimmed);
+    if (!result.ok) return;
+    const suggestion = `${result.parsed.channelName}-${tokenSuffix(result.parsed.token)}`;
     const current = form.getFieldValue('name');
     if (current && current !== lastAutoNameRef.current) return;
     form.setFieldsValue({ name: suggestion });
     lastAutoNameRef.current = suggestion;
   };
             {
               validator: (_rule, value) => {
                 const trimmed = _.trim(value || '');
                 if (!trimmed) return Promise.resolve();
-                const result = tryParseWebhookInput(trimmed);
-                return result.ok ? Promise.resolve() : Promise.reject(new Error(result.error));
+                return parseResult?.ok || !trimmed ? Promise.resolve() : Promise.reject(new Error((parseResult as { ok: false; error: string } | undefined)?.error));
               },
             },

(Note: validator timing vs. the watch-driven parseResult update should be double-checked; a simpler alternative is to keep the validator's own call but drop the separate suggestQuickRuleName call in handleUrlChange in favor of deriving the name from parseResult.)

As per coding guidelines, "Within a component, avoid repeatedly calling the same side-effectful transformation function; extract and reuse its result."

Also applies to: 56-63, 125-139

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/pages/notificationRules/components/RuleDropdownSelect/QuickCreateModal.tsx`
around lines 36 - 40, Reuse the memoized parseResult from the component’s
useMemo in both suggestQuickRuleName/handleUrlChange and the webhook URL
validator instead of calling tryParseWebhookInput repeatedly; ensure name
suggestion and validation use the current parsed value, accounting for
watch-driven update timing.

Source: Coding guidelines

@jsers
jsers merged commit eabc187 into main Jul 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants