From 4ca42e29af8ff6f612acae28b1a3ff2cb9afbb8a Mon Sep 17 00:00:00 2001 From: Shrinath Rao Date: Wed, 3 Jun 2026 14:00:27 +0530 Subject: [PATCH] feat: prebuilt template protection, per-destination templates, and correlation settings fixes (#12423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Combines #12414 (prebuilt template protection) and #12421 (correlation settings + AI i18n + traces fixes). ### Prebuilt template protection + per-destination template selection - Lets users attach a custom template to a prebuilt destination so multiple destinations of the same type (e.g. three Slack channels) can deliver different message bodies. - Protects system-managed \`prebuilt_\` templates from accidental deletion (403) while still allowing edits; create with a reserved name blocked (400). - Adds Prebuilt/Custom badges, All/Prebuilt/Custom toggle on both Templates and Destinations lists. - Per-row Clone action on templates (\`Copy_of_\` prefilled); Template column on destinations list. - Cross-type prebuilt template mismatches blocked in the dropdown (e.g. \`prebuilt_servicenow\` cannot be selected for a Slack destination). - Select-All fix in the shared OTable composable — \`toggleAllRows\`/\`isAllSelected\`/\`isIndeterminate\` now respect \`isRowSelectable\`. Design at: https://github.com/openobserve/designs/blob/main/alerts/prebuilt-template-protection/design.md ### Correlation settings visibility + AI i18n + traces tab fixes - **Correlation Settings tab** always visible for enterprise: removed unnecessary \`service_streams_enabled\` guard — route was always registered, only the nav item was incorrectly gated. - **Cross-alert fingerprint groups** hidden when org-level dedup is disabled; save payload sanitises \`alert_fingerprint_groups\` to \`[]\` when cross-alert dedup is off. - **AI suggestion cards** use i18n prompt keys (\`t('aiAssistant.capabilities.{id}.prompt')\`) instead of hardcoded English — prompt keys added to all 12 locale files. - **LLM Insights / Sessions tabs** always shown when \`showLLMUI\` is enabled; org-switch re-mounts both components via \`:key\`. - **LLMInsightsDashboard** layout, background, and empty-state alignment fixed. Fixes openobserve/o2-enterprise#1850, openobserve/o2-enterprise#1851, openobserve/o2-enterprise#1852 ## Changes | Layer | File | What | |---|---|---| | Backend | \`src/service/alerts/templates.rs\` | \`is_prebuilt_template_name()\` helper; block create with reserved name; block delete of prebuilt; edit allowed. | | Backend | \`src/service/db/alerts/templates.rs\` | New error variants: \`PrebuiltReadOnly\` (403), \`ReservedName\` (400). | | Backend | \`src/handler/http/request/alerts/templates.rs\` | HTTP error mapping + two unit tests. | | Backend | \`src/handler/http/models/destinations.rs\` | Public Template DTO carries \`isPrebuilt: bool\`. | | Frontend | \`web/src/components/alerts/AddDestination.vue\` | Template dropdown for prebuilt destinations with sentinel-backed Default option; edit-mode remap; i18n for all label strings. | | Frontend | \`web/src/composables/usePrebuiltDestinations.ts\` | \`templateOverride\` param on create/update; empty → backend default. | | Frontend | \`web/src/components/alerts/TemplateList.vue\` | Prebuilt/Custom badges, Clone action, delete-blocked for prebuilts, All/Prebuilt/Custom toggle, prebuilts excluded from Select All. | | Frontend | \`web/src/components/alerts/AddTemplate.vue\` | \`isClone\` prop — pre-fill without entering update mode. | | Frontend | \`web/src/components/alerts/AlertsDestinationList.vue\` | New Template column (ellipsis + tooltip, Default badge), All/Prebuilt/Custom toggle. | | Frontend | \`web/src/lib/core/Table/composables/useTableSelection.ts\` + \`OTable.vue\` | \`selectableRows()\` helper; Select All respects \`isRowSelectable\`; getter pattern restored for reactivity. | | Frontend | \`web/src/ts/interfaces/alert.ts\` | \`Template.isPrebuilt?: boolean\`. | | Frontend | \`web/src/components/alerts/OrganizationDeduplicationSettings.vue\` | Hide fingerprint groups when org dedup disabled; sanitise payload. | | Frontend | \`web/src/components/settings/index.vue\` | Remove \`service_streams_enabled\` guard from Correlation Settings nav item. | | Frontend | \`web/src/plugins/traces/Index.vue\` | Remove \`hasLLMStreams\` guard from LLM Insights / Sessions tabs. | | Frontend | \`web/src/plugins/traces/LLMInsightsDashboard.vue\` + \`SessionsList.vue\` | \`:key\` on org identifier for re-mount on org switch; layout fixes. | | Frontend | \`web/src/plugins/traces/SearchBar.vue\` | AI suggestion cards use i18n prompt keys. | | Frontend | \`web/src/components/ai-assistant/welcome/\` | Capability card prompts via i18n; all 12 locale files updated. | | Frontend | \`web/src/locales/languages/en.json\` (+ 10 other locales) | New keys: \`filter{All,Prebuilt,Custom}\`, \`templateDefaultBadge\`, \`prebuiltBadge\`, \`prebuiltBadgeHint\`, \`customBadge\`, \`clone\`, \`cloneTitle\`, \`systemReadOnly\`, \`templateDefaultOption\`, \`templateHelp\`; toast key moved to \`alert_destinations.saved\`. | ## Compatibility - **No schema migration.** "Is this a system template" is derived from the name matching \`prebuilt_\` where canonical types are the eight registered in \`config/prebuilt-destinations.json\`. - **No ENT changes needed.** Verified the enterprise tree does not reference \`TemplateError\`, the public Template DTO, or the new helper. - **API additive:** \`GET /alerts/templates\` response gains \`isPrebuilt: bool\`. - **API breaking (narrow):** - \`POST /alerts/templates\` rejects the eight canonical reserved names with 400. - \`DELETE /alerts/templates/{name}\` rejects the eight canonical names with 403. ## Test plan - [x] \`cargo test --lib -p openobserve -- prebuilt_read_only reserved_name\` — both pass - [x] \`cargo check --bin openobserve\` — clean - [x] \`vue-tsc --noEmit\` — exit 0 - [ ] Create 3 Slack destinations — each can be assigned a different custom template - [ ] Template dropdown for a Slack destination shows only custom templates + Default sentinel (no \`prebuilt_servicenow\` etc.) - [ ] Attempting to delete a \`prebuilt_\` template returns a 403 toast - [ ] Attempting to create a template named \`prebuilt_slack\` returns a 400 error - [ ] Clone button pre-fills \`Copy_of_\` and saves as a new template - [ ] Select All on a mixed prebuilt/custom template page: prebuilt rows unchecked, custom rows toggled - [ ] Enterprise build: Correlation Settings visible in sidebar without \`service_streams_enabled\` - [ ] Correlation Settings: disable org dedup → fingerprint groups section disappears; save → payload has empty \`alert_fingerprint_groups\` - [ ] Switch UI language → AI home suggestion cards send prompt in that language - [ ] Org with no LLM streams: LLM Insights + Sessions tabs still visible with correct empty state - [ ] Switch orgs on LLM Insights → data reloads for new org 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: github-actions[bot] --- src/config/src/prebuilt_loader.rs | 21 +++ src/handler/http/models/destinations.rs | 14 ++ src/handler/http/request/alerts/templates.rs | 21 +++ src/service/alerts/templates.rs | 23 +++ src/service/db/alerts/templates.rs | 10 ++ .../ai-assistant/welcome/CapabilityCards.vue | 6 +- .../ai-assistant/welcome/welcomeContent.ts | 8 +- web/src/components/alerts/AddDestination.vue | 98 +++++++++++- web/src/components/alerts/AddTemplate.vue | 20 ++- .../alerts/AlertsDestinationList.vue | 82 +++++++++- .../OrganizationDeduplicationSettings.vue | 5 +- .../components/alerts/TemplateList.spec.ts | 4 +- web/src/components/alerts/TemplateList.vue | 150 +++++++++++++++++- web/src/components/settings/index.vue | 2 +- .../composables/usePrebuiltDestinations.ts | 29 +++- web/src/lib/core/Table/OTable.vue | 4 + .../Table/composables/useTableSelection.ts | 35 ++-- web/src/locales/languages/de.json | 31 +++- web/src/locales/languages/en.json | 36 +++-- web/src/locales/languages/es.json | 31 +++- web/src/locales/languages/fr.json | 31 +++- web/src/locales/languages/it.json | 31 +++- web/src/locales/languages/ja.json | 31 +++- web/src/locales/languages/ko.json | 31 +++- web/src/locales/languages/nl.json | 31 +++- web/src/locales/languages/pt.json | 31 +++- web/src/locales/languages/tr.json | 31 +++- web/src/locales/languages/zh.json | 31 +++- web/src/plugins/traces/Index.vue | 2 + .../plugins/traces/LLMInsightsDashboard.vue | 43 +++-- web/src/plugins/traces/SearchBar.vue | 10 +- web/src/plugins/traces/SessionsList.vue | 11 +- web/src/ts/interfaces/alert.ts | 4 + 33 files changed, 787 insertions(+), 161 deletions(-) diff --git a/src/config/src/prebuilt_loader.rs b/src/config/src/prebuilt_loader.rs index 6579d0bb4b..ef96a4439f 100644 --- a/src/config/src/prebuilt_loader.rs +++ b/src/config/src/prebuilt_loader.rs @@ -108,6 +108,27 @@ fn load_prebuilt_config() -> PrebuiltDestinationsConfig { load_builtin_config() } +/// Prefix that marks system-managed template names. The full reserved set is +/// `prebuilt_` where `` is one of the registered prebuilt +/// destination types — see `is_prebuilt_template_name` below. +pub const PREBUILT_TEMPLATE_PREFIX: &str = "prebuilt_"; + +/// True only for template names the system actually manages: the name must be +/// `prebuilt_` where `` is a registered prebuilt destination type +/// (slack, opsgenie, servicenow, ...). +/// +/// Lives next to `get_prebuilt_template` so the public API surface +/// (`handler::http::models`), the service-layer guards +/// (`service::alerts::templates`), and any future caller all derive the +/// "is system template" answer from the same source of truth. +/// +/// User-created templates whose names happen to start with `prebuilt_` but +/// don't match a registered type stay freely editable and deletable. +pub fn is_prebuilt_template_name(name: &str) -> bool { + name.strip_prefix(PREBUILT_TEMPLATE_PREFIX) + .is_some_and(|t| get_prebuilt_template(t).is_some()) +} + /// Get a specific prebuilt template by type (e.g., "slack", "teams", "email") pub fn get_prebuilt_template(prebuilt_type: &str) -> Option + +