mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 00:09:35 +00:00
fd2306ffaa3ec2d760f05df3fcf3791efb1ff991
6875
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fd2306ffaa |
fix(search): preserve indexed top-n aggregation results (#13779)
- close: https://github.com/openobserve/openobserve/issues/13363 ## Summary - Prevent `AggregateTopkExec` from truncating file-local indexed aggregation rows before the final aggregate merges duplicate group keys. - Keep the Tantivy `SimpleTopN` optimization enabled while removing only the conflicting downstream top-k node. - Add a regression test that constructs the conflicting physical plan, verifies `AggregateTopkExec` is present before leader index optimization, and verifies it is removed afterward while `SimpleTopN` remains selected. ## Root cause `SimpleTopN` emits partial count rows per `(file, group key)`. `AggregateTopkExec` previously ranked and truncated those individual rows before the final aggregate could combine identical keys across files. With enough files, this silently undercounted results and removed entire groups while `is_partial` remained false. The rewrite removes the conflicting node bottom-up. This is important because `AggregateTopkExec` is inside the follower plan already serialized by `RemoteScanExec`; rebuilding the parent updates those serialized plan bytes before attaching `SimpleTopN`. ## Verification Using 121 Parquet files and 121 Tantivy index files containing 93,601 records across 12 groups: | Result | Before | After | |---|---:|---:| | Groups returned | 9 | 12 | | Sum of grouped counts | 83,201 | 93,601 | | Baseline `COUNT(*)` | 93,601 | 93,601 | | `is_partial` | false | false | Tests run: - `cargo fmt --all -- --check` - `cargo test -p search test_leader_rule_topn_removes_conflicting_aggregate_topk -- --nocapture` - `cargo test -p search datafusion::optimizer::physical_optimizer::index_optimizer::tests -- --nocapture` - `cargo test -p search aggregate_topk -- --nocapture` - `cargo check -p search` |
||
|
|
3d06ed0423 |
fix(logs): reject LIMIT in filter mode instead of building an invalid query (#13773)
Closes #6915. Without SQL mode the search bar's contents become the WHERE body of a generated `select [FIELD_LIST][QUERY_FUNCTIONS] from "[INDEX_NAME]" [WHERE_CLAUSE]`, and `handleNonSqlMode` splices the input in verbatim after stripping comments and quoting known field names. Nothing checks that the input is actually a predicate, so `k8s_namespace_name = 'nginx-ingress' LIMIT 10` produces valid SQL. The results grid runs it and returns rows, while the histogram query built from the same input is rejected by `is_eligible_for_histogram` and surfaces `SearchHistogramNotAvailable`. That is the split described in the issue — the log list works, the histogram errors. The server side of this already exists, so the guard belongs in the UI, before either request goes out. ## What changed | | | |---|---| | `utils/query/nonSqlLimit.ts` | new — `hasLimitClause()` | | `useSearchQuery.ts` | the guard in `handleNonSqlMode`, using the file's existing `notificationMsg` + `return null` path | Placement note: `hasLimitClause` lives in `utils/query/` rather than in the composable because it holds no state and touches no reactivity — the same split `doubleQuoteWarnings.ts` and `colorizeQuery.ts` already use. The check is deliberately narrow. `LIMIT` matches only when followed by a number, so `limit = 5` and `rate_limit >= 100` pass through. String literals are blanked first, so `msg = 'rate limit 500 exceeded'` is unaffected. Line and block comments are stripped after that, so `code = 200 -- limit 10` — which works today — keeps working. ## Tests - **17** on the derivation — the issue's own query, case variants, whitespace and newlines before the number, a bare `LIMIT 100`, a field named `limit`, `rate_limit >= 100`, the word inside a literal, a literal with a doubled quote, `limit = 'none'`, leading / trailing / block comments, a real LIMIT alongside a comment, and a dash sequence inside a literal. - **6** on `buildSearch` — rejects the reported query and sets the message, still builds without a LIMIT, allows `limit = 5`, ignores commented-out LIMITs, and does not fire in SQL mode. Each rejection test was checked to fail when the guard is reverted. The negative cases stay green either way, which is what separates them from tests that merely restate the implementation. ## Verification - `npx vitest run --root src/ composables/useLogs utils/query` — **1965 passed**, 33 files, 0 failed - `npx vue-tsc --noEmit -p tsconfig.vitest.json` — clean, and identical to `main` - `npx eslint` on all four files — clean - `npx prettier --check` — clean ## Caveats - Not exercised in a running app; covered by unit tests only. - `ORDER BY`, `GROUP BY` and `HAVING` reach the same splice and are **not** handled here. Happy to extend if you want them, but the issue asked about `LIMIT` and I didn't want to widen it uninvited. - `plugins/logs/IndexList.vue` holds an independent copy of the same `[WHERE_CLAUSE]` splice for field-value lookups. It is reached by clicking a field rather than running a search, so it sits outside this path — flagging it rather than changing it. --------- Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
6650950a50 |
test(e2e/alpha1): fix share-link redirect origin and cloud metrics ingest auth (#13776)
## Why The scheduled **Alpha1 (Cloud)** e2e run failed on three jobs ([o2-enterprise run 31547517666](https://github.com/openobserve/o2-enterprise/actions/runs/31547517666)): **Logs-Features** (7), **Functions** (11), **RegressionSet** (4). Root-caused each against a freshly-deployed alpha; two are genuine test heals, one was pure environment skew. ## Root causes & fixes ### 1. Logs-Features — shareLink + monaco-query-prefill (7 tests) ✅ fixed On cloud the app builds the share link from its **public** base URL (`*.external.zinclabs.dev`), while the tests authenticate against the ingress host (`*.internal.zinclabs.dev`). `page.goto(sharedUrl)` navigated **cross-origin**, dropped the auth cookies, and bounced to Dex — so every post-redirect state assertion read back `null`. **Fix:** re-point the short URL's origin to `ZO_BASE_URL` in `clickShareLinkAndGetUrl()` (right next to the existing HTTP→HTTPS "maintain auth context" step). `/short/<id>` resolves identically on the backend regardless of ingress host, so only the origin changes and the session stays valid. Same-origin envs (localhost / matching deployed host) are untouched. ### 2. RegressionSet — alerts Bug #9967 PromQL (1 test) ✅ fixed `ingestMetricsData()` authenticated with `ZO_ROOT_USER_EMAIL:PASSWORD`, but **cloud ingestion needs `email:passcode`**. The ingest silently 401'd → the `e2e_test_cpu_usage` metrics stream was never created → the PromQL alert flow timed out selecting it. **Fix:** build auth via `getAuthHeaders()` (passcode on cloud, root creds on self-hosted), refresh the passcode from the live browser session first, and poll until the stream is indexed so the wizard's stream-select doesn't race WAL lag. ### 3. Functions — radio → segmented toggle (11 tests) — no change needed The page object was already migrated to the segmented toggle in #13766; these failures were the **alpha frontend lagging the merged test code**. Green after the redeploy with zero code change. ## Verification (against alpha, `auto_org_1`) - `shareLink.spec.js` + `monaco-query-prefill.spec.js` → **11/11 passed** - `alerts-regression.spec.js` (full) → **5/5 passed** (#9967, #10110, #10899, #10872, #10472) - `Functions/js-transform-type.spec.js` → **7/7**, `row-expansion.spec.js` → green ## Scope OSS-only (test files). The ENT `playwright_alpha1.yml` checks out opensource at `main`, so merging makes the next scheduled run pick this up. It can also be pre-verified by dispatching that workflow with `o2_opensource_repo=test/alpha1-e2e-heals-0812`. |
||
|
|
fb50aaea7f |
fix(deps): resolve 39 Dependabot alerts (#13774)
## Summary - update vulnerable web transitive dependencies (`axios`, `brace-expansion`, `fast-uri`, `http-proxy-middleware`, `js-yaml`, `uuid`, and `websocket-driver`) - remove the unused `node-polyfill-webpack-plugin`, eliminating the unpatched `elliptic` dependency chain - update Playwright to 1.55.1 and `node-forge` to 1.4.0 in the UI test lockfile - adapt `BaseImport` to Axios 1.18's stricter response-header type This is expected to resolve 39 of the 41 currently open Dependabot alerts. ## Remaining alerts - `rmcp` remains on the project's custom `rmcp-openapi` fork. Updating it safely requires a coordinated change to the enterprise `Cargo.toml.openobserve`, which currently replaces the OSS manifest during enterprise builds and pins the old API. The compatible intermediate upstream releases also lack the fork's required `allOf`/`anyOf` support. - `thrift` 0.17.0 remains through `parquet` 58.4.0. The patched Thrift line (0.23+) is outside Parquet 58's version constraint. Parquet 59 removes the Thrift dependency, but adopting it requires a coordinated Arrow/DataFusion 59 upgrade. ## Validation - `npm ci` and `npm audit --package-lock-only` in `web/` (0 vulnerabilities) - `npm ci` and `npm audit --package-lock-only` in `tests/ui-testing/` (0 vulnerabilities) - `npm run type-check:app` - `npm run lint:ci -- --quiet` - `npm run lint:design:strict` - `NODE_OPTIONS=--max-old-space-size=8192 npm run build` - `npm run test:unit -- --run` (41,917 passed, 404 skipped) - `npx vitest run src/components/common/BaseImport.spec.ts --environment jsdom` (41 passed) - Playwright test discovery under 1.55.1 (2,076 tests in 265 files) - `cargo fmt --all -- --check` - `cargo test -p openobserve-mcp` (94 passed, 1 ignored) - `cargo audit` (0 vulnerabilities; 2 allowed unmaintained warnings) |
||
|
|
c451db0835 |
fix: tighten workflow permissions and CSS selector escaping (#13772)
## Summary - set explicit least-privilege permissions for audit, JavaScript license, and release workflows while preserving release-job write access - grant the spam-cleaner workflow the issue and pull-request scopes required to delete comments - escape quotes and backslashes before interpolating log toast text into CSS attribute selectors ## Validation - parsed the four modified workflow files as YAML - ran Node syntax checking on tests/ui-testing/pages/logsPages/logsPage.js - ran git diff whitespace validation |
||
|
|
360b989272 |
feat(slos): PromQL support for count and time-sliced SLOs, plus SLO/alert regression fixes (#13761)
Supersedes #13738, whose commit is included here as the first of four. Adds PromQL support to count and time-sliced SLOs, lets metrics streams choose SQL or PromQL, and fixes three shipped bugs found along the way. ## Shipped bug: every SQL time-slice SLO recorded wrong history `fetch_rows`' `SliQueryPlan::Single` arm normalized every hit with `with_good_column: true`, but that arm serves **two** shapes and only one projects `zo_slo_good`: | shape | projects | result | |---|---|---| | count single-query | `zo_slo_good` + `zo_slo_value` | correct | | time slice | `zo_slo_value` only | `good` defaulted to `0.0` | So every SQL time-slice slice was classified from `0.0` instead of its aggregate. With a positive threshold, `<`/`<=` (e.g. `p95 < 300ms`) recorded every slice **good** — the SLO read ~100%, the budget never burned, and no alert on it could fire. `>`/`>=` recorded every slice **bad** and fired continuously. Shipped in v0.92.0 (`4772df1723`); `to_row` had no time-slice test, which is how it got through. Fixed by deriving the flag from the SLI type in a new `single_query_rows`. Deliberately **not** fixed inside `to_row` by falling back to `total` — that would read a count bucket whose `SUM` came back NULL as *fully good*, inventing uptime. A test forbids it. **Stored history is not rewritten.** It ages out within one window; an operator can force a rebuild sooner by making a real edit (a no-op re-save does nothing — `update` bumps only on `definition_changed`). No auto-migration: bumping every time-slice SLO at boot would freeze every attached alert org-wide until backfill cleared the coverage floor, and double each org's row budget for 97 days — a self-inflicted monitoring gap to repair a reporting error. ## PromQL support **Time slice** carried a `query_language` field the planner ignored, so a PromQL time-slice SLO saved cleanly and measured nothing, forever. - `SliQueryPlan::PromQlValue` — one expression, not a good/total pair, planned at slice ends from the same helper the count arm uses. - The expression is passed through **verbatim**. Grouping comes from series labels; wrapping it in `sum by (...)` the way the SQL arm injects GROUP BY would destroy the aggregate (the sum of four pods' p95 is not a p95). - `promql_value_rows` **rejects** two series colliding on one `(slice_start, group_key)` via `RejectReason::AmbiguousSeries` instead of summing. Summing is right for counters (what `promql_rows` does) and meaningless here. Detection is unconditional, so the ungrouped case — an unaggregated `histogram_quantile` returning one series per instance — is covered. Rejects are plumbed through `fetch_rows` into `PassResult.rejected` on both the live and backfill paths. **Count** already had a working `CountSource::PromQl` backend that no UI could reach. ## Metrics streams choose SQL or PromQL `language_suits_stream` refused SQL whenever `stream_type == "metrics"`, and the form derived the language from the stream type — so metrics had no SQL option. A metrics stream is an ordinary stream with a `value` column, and `SELECT histogram(_timestamp, '5 minute'), avg(value) FROM cpu_usage` is a perfectly good SLI. SQL is now valid for any stream type; PromQL stays metrics-only. The form asks via a toggle (defaulting to PromQL) instead of deriving. ## Validation - `scope` refused for PromQL — a SQL `WHERE` fragment with nowhere to go in a PromQL plan. - Empty `query` refused for **both** languages (the SQL aggregate was previously never validated — same silent-freeze shape). - Expressions parsed with `promql-parser` at save, in `core` (`config` has no parser). - **Parse-only by design.** A grouping check requiring the root to be an aggregation whose `by(...)` matches `group_by` wrongly rejects `histogram_quantile(0.95, sum by (le, region)(...))` (function-call root; `le` is a necessary label superset) and binary-op roots like `sum by (region)(increase(a[5m])) - sum by (region)(increase(b[5m]))`. A "warn instead" variant needs a warnings channel the save API doesn't have. ## Also fixed - **Time-slice comparator 422.** `SliConfig::TimeSlice.comparator` has no serde default, so a dropdown the user never opened was omitted and the save died with a bare 422 — blocking creation of *any* time-slice SLO from the UI. Seeded to `<` only when blank, so a stored comparator survives an edit. - **SLO/alert/service-stream regressions** (the commit from #13738): fractional-threshold serde round-trip, `SloAlertCondition` UI, alerts table, service streams. ## Verification - 487 config · 206 core · 45 infra · 418 frontend — all passing; clippy `-D warnings`, `cargo fmt`, prettier, eslint, `vue-tsc` all clean. - Driven through a real browser against a running server (isolated instance, own data dir). All four SLI/language combinations create and store correctly: count·PromQL, count·**SQL over metrics**, time-slice·PromQL, time-slice·**SQL over metrics**. Also verified: the toggle swaps field shapes both ways, scope shows for SQL and hides for PromQL, PromQL-only hints appear only in PromQL mode, and editing a stored SQL-over-metrics SLO reopens as SQL with its predicate intact. - Validation rejections confirmed live with actionable messages: PromQL-with-scope, unparseable PromQL, and an empty SQL aggregate. Known limitation, pre-existing: `threshold` likewise has no serde default and is not seeded, so leaving it blank still 422s. That is genuine user error rather than an untouched-control trap; adding form validation was out of scope. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
9f76ff6388 | refactor(search): enhance filtering logic for FTS fields in search function (#13767) | ||
|
|
b07b3d4229 |
feat(functions): replace language radios with a segmented toggle (#13766)
The Add Function header selected VRL vs JavaScript with a radio group and a single info icon. That icon only ever described the ALREADY-SELECTED language, so learning what the other one was for meant switching to it first. - Radio group -> OFormToggleGroup segmented control with a sliding indicator, each option carrying a language mark (V / JS). The badge variants match the transType column in FunctionList so VRL/JS read the same everywhere. - Give EACH option its own info icon and static tooltip, so both languages are readable without selecting either. Forced-language hosts (workflow function nodes are JS-only) hide the toggle, so the locked language's tip moves to a standalone icon. - Add a pipe divider between the two options, and one between the function-name title and the toggle. data-test renames `...-vrl-radio` -> `...-vrl-option` (same for js): "radio" described the old implementation. E2E page objects would have broken on this change regardless of naming -- toggle items expose data-state="on|off", not the radio group's "checked|unchecked". Verified: FunctionsToolbar.spec.ts 18/18 pass, vue-tsc clean. ESLint could not be run locally (@intlify/eslint-plugin-vue-i18n missing from node_modules). |
||
|
|
bf27a488d6 |
fix(ci): stop retrying translation pushes that a merge queue has frozen (#13765)
`auto_merge_enabled` is one of the two events that start this workflow,
and it fires on the same click that enqueues the PR. GitHub freezes a
queued PR's branch, so by the time the run finishes translating, the
push it exists to make is rejected with GH006.
The push loop then made the failure much harder to read than it needed
to be. It was written for one failure mode -- losing a race to a
concurrent push -- so it treated every rejection as a lost race and
rebased. GH006 is a policy rejection, not a race: the rebase is a no-op
("Current branch ... is up to date") and the next two attempts fail
identically, burying the real reason under a log that looks like
flakiness.
- Classify the rejection at push time. GH006 / merge-queue /
protected-branch stops on the FIRST attempt with an error naming the
cause and the fix (dequeue, re-run, re-queue). Non-fast-forward keeps
the existing rebase retry.
- Check for queue membership in the guard, before any API spend. When
the PR is already queued the run cannot land a commit, so translating
would spend the DeepSeek budget building one with nowhere to go.
verify-translations.yml still gates the queue, so a genuinely
untranslated PR is still blocked from main.
Fail-open on the queue lookup: a transient API error falls through to
"not queued" so a blip can never silently stop translations from being
generated. The push-time check backstops it.
No keys are lost in either path -- .translation_state.json still lists
them as pending, so the next run picks them up.
|
||
|
|
4b9204f69f |
feat(synthetics): collapse journey advisories into an on-demand suggestions chip (#13759)
## Problem
The browser journey editor stacked two **always-on** advisory cards
between the toolbar and the first step:
- *"This journey does not verify anything"* — no `assert` step in the
journey
- *"No test attributes found in this recording"* — no step produced a
`test_attribute` locator candidate
Both derived their visibility from the journey's own content, and both
conditions are the normal case for a real recording — so they rendered
on every visit to every affected journey. Each was dismissible, but
dismissal lived in a local `ref(false)`, so it died on remount: authors
closed the same two cards on every visit and still began reading their
steps two cards down the page.
## Change
One warning badge in the Steps toolbar — a warning icon and a count, no
prose:
- **Hover** names it ("1 suggestion for this journey"), from the same
string as the trigger's `aria-label` so the two cannot drift.
- **Click** opens a popover with each suggestion in full, separated by
rules, plus a footer stating that suggestions never block saving or
running.
- **Never opens on its own**, on any render.
- **No dismiss.** The chip costs no vertical space, so suppressing it
buys nothing — and a suggestion that can be waved away is one an author
never has to actually decline. A suggestion leaves when the condition
behind it is resolved.
A zero-assertion journey remains accepted with a warning; nothing here
blocks a save.
Transient banners are deliberately untouched — replay
running/passed/failed/stopped, incognito, pre-flight, recorder error.
They appear because something just happened, and a replay result that
does not announce itself is a worse product.
## Implementation
| File | |
|---|---|
| `web/src/utils/synthetics/journeySuggestions.ts` | new — pure
derivation over the steps; one small producer per rule; carries i18n
**keys** rather than text, so it needs neither Vue nor `useI18n` and a
rule is testable without mounting anything |
| `web/src/components/synthetics/journey/JourneySuggestions.vue` | new —
chip, tooltip, popover; presentation only |
| `web/src/components/synthetics/journey/BrowserJourney.vue` | computes
the suggestions, renders the chip in the toolbar, appends the offered
assertion |
| `ZeroAssertionNotice.vue`, `TestIdMisconfiguredNotice.vue` (+ specs) |
deleted — each was a visibility computation plus a card, and both halves
now have better homes |
Adding a future advisory is one producer and no template change.
Placement note: this lives in `utils/synthetics/` rather than a
composable because it holds no state and touches no reactivity — the
same split `deriveLocatorKind.ts` and `locatorStability.ts` already use
for derivations consumed by these components.
## Tests
- **17** on the derivation — both rules, empty journey, navigate-only
journey, empty candidate list, a test attribute outranked *within* a
step, both conditions at once in stable order, and that the offered
assertion is empty (`element_visible` with no selector/locator) rather
than guessed.
- **15** on the component — no chip at zero, count only and no prose,
pluralization, closed on mount, opens on click, tooltip suppressed while
open, action emits and closes, separators between suggestions.
- **5** on the `BrowserJourney` wiring — old hooks gone, suggestions
handed down, nothing offered when `readonly`, the assertion appended to
the tail with prior step order and ids intact, and the suggestion
dropping once the parent commits.
Two things the tests caught that review would not have:
1. Resolving the last suggestion unmounts the popover but not the
component holding its `open` flag — so a panel left open came *back*
open the next time a suggestion appeared. Regression test written,
confirmed failing, then fixed.
2. One component block runs against the **real** `OPopover` and `OBadge`
instead of a stub. `OBadge` declares `inheritAttrs: false` and the
popover trigger is `as-child` — precisely the pairing that silently
drops a click handler and leaves a chip that looks correct and does
nothing. Every other test stubs the popover and would not see it.
## Accessibility
`⚠ 1` has an accessible name of "1", so the trigger's `aria-label` is
load-bearing and carries the full count string. Child-mode tooltips are
pointer-only, so a keyboard user's path is that label plus the popover
they open with Enter.
Accepted trade-off, stated plainly: the old cards carried
`role="status"` and were announced on appearance. Nothing is announced
now — that announcement is what this change exists to suppress.
## Verification
- `npx vitest run src/components/synthetics src/utils/synthetics
src/views/synthetics` — 1147 passed, 58 files
- `npm run type-check` — clean
- `npm run lint` — clean for every touched file (the repo's pre-existing
`eslint.config.js` / `vitest.config.ts` findings are unchanged and
present on `main`)
|
||
|
|
de0d37e23d |
fix(alerts): stop losing Slack alerts on undeliverable link URLs (#13742) (#13753)
Fixes #13742 Fixes openobserve/o2-enterprise#2394 ## The bug A content template link with a non-http scheme (`javascript:alert(1)`) made Slack reject the **entire** notification with `400 invalid_attachments`. The alert was lost outright and `triggers` recorded `notify_failed`. ## Root cause `safe_url` neutralises a hostile scheme by substituting `BLOCKED_URL` (`"#"`). That is **safe** but not **deliverable** — Slack, Teams, Discord and PagerDuty validate action URLs server-side and reject the whole payload when one is not an absolute `http(s)` URL. A security substitution was turning into total message loss. The two properties are different, and links need both. So the fix splits by medium: | renderer | link handling | |---|---| | Slack buttons, Teams actions, Discord `embed.url`, PagerDuty `href` | `dispatchable_url` → **drop** | | HTML email, email plaintext, SNS plaintext, Discord description | `safe_url` → substitute `#` | | Webhook envelope | unfiltered — versioned machine contract, documented | Dropping beats rewriting: the alert body is worth more than the button, and a button pointing somewhere misleading is worse than no button. `dispatchable_url` requires a **non-empty authority**, not just an allowlisted scheme — `http:javascript:alert(1)` and `https://` are valid *opaque* RFC 3986 http(s) URLs that Slack still rejects. It also rejects raw spaces and control characters, which variable substitution can introduce. ## Related issues fixed in the same area - **Empty `ZO_WEB_URL`** (not in the report): `alert_url` becomes a root-relative `/web/logs?…`, which `safe_url` passes through by design, and it lands in the always-appended "View in OpenObserve" button. Same `invalid_attachments`, on deployments that never authored a hostile template. - **PagerDuty** validates `links[].href` server-side too — same lost-alert class, converted. - **Save-time rejection** (`ContentSpec::validate`): a clear 400 naming the offending link, instead of a template whose link silently never works. Also wired into **preview** and **test-send**, which previously accepted a draft that couldn't be saved and echoed the raw hostile URL back to the UI. - **Slack image-fallback misdiagnosis**: removing the image proves the image was at fault, but only an image pointing at *our* `web_url` justifies tripping the process-wide chart suppression (1h, across every alert) and telling the operator to fix `ZO_WEB_URL`. A custom template's third-party image no longer does either. - **a11y**: `OInput` links its error message to the control via `aria-describedby`; the textarea branch also gained `aria-invalid`. ## o2-enterprise#2394 (fixed here, not in the enterprise repo) Clearing the Monaco body and saving toasted "Please fill required fields" with no indication of *where*. The schema already produced an error on the `body` path — a bare Monaco bridged in via `setFieldValue` just had no `OFormInput` error slot to render it. Now a red border + message driven by the same field state `OFormInput` reads. The aria wiring goes on the **shell**, not on `<QueryEditor>`: `CodeQueryEditor` sets `inheritAttrs: false` and binds `$attrs` to a non-focusable wrapper, so attributes passed to the component would be inert — false assurance for a screen-reader user. > The issue names the title field too. It's an `OFormInput` and already highlights correctly; I added a regression test pinning that rather than changing it. This fix is entirely in OSS (`web/`) since the template form isn't enterprise-gated, so there's no companion enterprise PR. ## Layering note Save-time validation is a **UX gate, not the security boundary**. Link URLs are templates — a scheme that only materialises after `{variable}` substitution can't be judged at save time. The render layer is what actually stops delivery, and it also covers every template saved before this validation existed. Both layers are needed; neither is sufficient. ## Testing - Every fix written test-first (verified RED) and **mutation-tested** — each new test fails when its fix is reverted in substance. - Golden files unchanged: legitimate URLs render byte-identically, so no over-blocking. - Rust: `alerts::notifications` 159 · `config` 3130 · `api-management` 424 — all passing. `cargo clippy --all-targets` and `cargo fmt --check` clean. - Frontend: full suite **41,597 passing** (run because `OInput` is shared library code). `vue-tsc`, `eslint`, and `npm run format:check` clean. - Two independent cold-start review passes; findings from both are folded in, including a P0 where the first version of `dispatchable_url` still let the original bug through. Pre-existing and unrelated: `alerts::destinations::tests::test_alert_destination_template_is_optional` fails locally on a DB connection reset — verified failing on unmodified `main` too. |
||
|
|
883fc97046 |
fix(dashboard): deduplicate panel and layout IDs on import and dashboard load (#13760)
https://github.com/openobserve/openobserve/issues/13104 |
||
|
|
4a837ee4b9 |
chore(config): remove unused environment variables (#13690)
## Summary - remove 10 EnvConfig entries that have no runtime consumers - stop advertising ineffective knobs across SLO, common, runtime, query, metadata, NATS, S3, and pipeline configuration - preserve active replacements such as ZO_FAKE_ES_VERSION and ZO_PIPELINE_BATCH_SIZE_BYTES These variables were parsed but never read, so this does not change runtime behavior. ## Testing - cargo fmt --all - cargo check -p config - cargo test -p config config::tests --lib (64 passed) - cargo clippy -p config --lib -- -D warnings --------- Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
d5fa24711c |
perf(metrics): choose label timestamp filter automatically (#13756)
## Summary - replace the `ZO_METRICS_INLIST_FILTER_ENABLED` switch with automatic timestamp filter selection - use an exact `IN` filter for up to 1024 timestamps and a bounded `BETWEEN` predicate above that limit - sort exact timestamp literals for stable query plans - add boundary coverage for empty, threshold, and over-threshold timestamp sets ## Rationale Large timestamp `IN` lists add planning and evaluation overhead, while always using `BETWEEN` can admit many unrelated label rows for sparse timestamp sets. A bounded exact-list strategy keeps the selective path for manageable sets without allowing query plans to grow indefinitely. Release benchmarks used 4.2M rows, five label columns, four hash partitions, and repeated median measurements: - with a representative sparse range returning about 262K rows through `BETWEEN`, 1024 timestamps favored `IN` (9.1 ms vs 10.1 ms), while 2048 favored `BETWEEN` (11.7 ms vs 10.2 ms) - when unique maximum timestamps equaled the series count and both predicates returned exactly one row per series, `BETWEEN` consistently won and the gap grew with list size, confirming the need for an upper bound ## Testing - `cargo test -p promql series_labels::tests` - `cargo clippy -p promql --tests --no-deps -- -D warnings` - `git diff origin/main...HEAD --check` ## Operational note `ZO_METRICS_INLIST_FILTER_ENABLED` is removed and no longer needs to be configured. |
||
|
|
786f4f9f72 |
fix: dashboard label overlap issues (#13725)
1. <img width="727" height="242" alt="image" src="https://github.com/user-attachments/assets/2a84c1bf-2bc0-4fa7-aae5-e7801b99c93b" /> 2. <img width="167" height="380" alt="image" src="https://github.com/user-attachments/assets/6d937583-eb51-458c-8dac-fcf668f1f145" /> 3. <img width="1072" height="150" alt="image" src="https://github.com/user-attachments/assets/4f98ff15-06e4-45d2-a109-c84064cb644b" /> 4. <img width="32" height="289" alt="image" src="https://github.com/user-attachments/assets/9e4ef37f-2e9a-4259-9c6e-041946e1e380" /> 5. <img width="500" height="622" alt="image" src="https://github.com/user-attachments/assets/c4566094-6f58-4692-9ce8-bbcaf61026d7" /> 6. <img width="388" height="644" alt="image" src="https://github.com/user-attachments/assets/03e14424-a59a-4738-b0aa-1f70c7d207f6" /> 7. <img width="388" height="564" alt="image" src="https://github.com/user-attachments/assets/a1acded3-b2d6-4690-b2ba-47397f6a3fb4" /> |
||
|
|
4531ae74d8 |
test(dashboards): e2e coverage for favorites cross-folder search fix (#13437) (#13758)
## Summary Adds E2E coverage for #13437 (`fix: dashboard all folder search for favorites`). That PR changed three things in `Dashboards.vue`; this adds one test per change. ## Test cases added | # | Test case | Covers | Priority | |---|-----------|--------|----------| | 1 | `should surface a non-favorited dashboard in the Favorites folder when searching across folders` | Favorites view yields to an active cross-folder search instead of short-circuiting to the stored favorites list | P0 | | 2 | `should restore the favorites-only list after clearing a cross-folder search` | Clearing resets both scope models (`searchQuery` + `filterQuery`), so the favorites-only list comes back | P1 | | 3 | `should expose a clear button for a current-folder search and restore the full list` | Search field is unconditionally `clearable` — the clear (X) button now renders in "This folder" scope too | P1 | ## What each test proves **1.** Seeds two dashboards in one folder, favorites only the first. Asserts the un-favorited one is **absent** from the Favorites view, then switches the scope toggle to "All folders", searches it by name, and asserts it appears. Before the fix the rows computed returned `favorites.value.map(...)` whenever the `__favorites__` pseudo-folder was active, so a cross-folder hit could never render. **2.** Runs that same cross-folder search, then clicks clear and asserts the field is empty, the searched-for dashboard drops out, and the favorite is listed again. **3.** Searches in the default "This folder" scope, asserts the filter is active (second dashboard filtered out), that `[data-test="dashboard-search-clear"]` is visible, and that clicking it empties the field and restores both rows. Previously `:clearable="searchAcrossFolders"` meant no clear button existed in this mode at all. ## Files changed - `tests/ui-testing/playwright-tests/dashboards/dashboard-favorites.spec.js` — 3 new tests (file now has 9) - `tests/ui-testing/pages/dashboardPages/dashboard-favorites.js` — 3 locators (`searchScopeAllFolders`, `searchScopeCurrentFolder`, `searchClearBtn`) + 5 methods (`searchAcrossFolders`, `clearSearch`, `verifySearchClearButtonVisible`, `verifySearchInputEmpty`) ## Test design Each test seeds a second, deliberately un-favorited dashboard (`DashB_<ts>`), named so neither dashboard's name is a substring of the other — that second row is the only thing that can prove the search reached past the favorites list. Every test asserts its own baseline *before* the action, so a pass is attributable to the change under test rather than to a permissive list. All selectors read from source; no new `waitForTimeout` sleeps; cleanup covered by the existing `afterEach`. ## Test results `9 passed` locally against `localhost:5080`. New tests verified green on repeated isolated runs. ## Caveats - Tests were **not** verified to fail against pre-fix code — the app is served from a prebuilt bundle (no dev server), so reverting the hunks would require a full frontend rebuild. The in-test baseline assertions mitigate this but don't replace it. - This spec file is **self-hosted-only**: `login()` in `dashLogin.js` waits for the `login-user-id-field` form, while alpha cloud uses Dex SSO. Confirmed pre-existing (an untouched test in this file fails at the identical line), but it means these tests won't run in a cloud-targeted CI job. |
||
|
|
238b0d5262 |
fix: support zstd-compressed HTTP requests (#13728)
## Summary - enable tower-http zstd request decompression - add a router middleware regression test using a real zstd-compressed JSON payload - update decompression documentation to list zstd support ## Root cause The Axum router uses `RequestDecompressionLayer`, but the workspace did not enable tower-http's `decompression-zstd` feature. Requests from clients such as Vector configured with `compression: "zstd"` were therefore rejected with `415 Unsupported Media Type` before reaching the ingestion handler. ## User impact Vector HTTP sinks can continue using zstd compression when sending batches to OpenObserve ingestion endpoints. ## Validation - `cargo fmt --all -- --check` - `cargo test -p openobserve-api-http handler::http::router::decompression::tests` - `git diff --check` Fixes #13726 |
||
|
|
56bedcb126 |
test(logs): fix flaky pattern-card click under OVirtualScroll windowing (#13752)
## Summary Fixes the flaky failure seen in ENT PR #2386's `e2e / Logs-Features` shard: ``` Logs/searchPatterns.spec.js:293 › should open pattern details dialog when clicking pattern card TimeoutError: locator.click: Timeout 45000ms exceeded. - waiting for locator('[data-test="pattern-card-0"]') ``` Failed all 4 attempts, took the rest of the shard down (`1 failed, 10 did not run, 57 passed`). **Unrelated to #2386's LLM-annotation changes** — it's pre-existing test-infra. ## Root cause `PatternList` renders cards inside `OVirtualScroll`, which stamps the **absolute** item index onto each card (`pattern-card-<absoluteIndex>`) and mounts **only the visible window**. The card getters were already migrated to address cards by *rendered position* (`patternCardAt()` / `patternCardPart()` — suffix + `.nth()`), but three code paths were left on the absolute-index selector: - `clickPatternDetailsIcon()` → clicked `[data-test="pattern-card-${index}"]` - `clickPatternIncludeBtn()` / `clickPatternExcludeBtn()` - the `waitForPatternsToLoad()` `'patterns'` visibility check When the mounted window starts above index 0, `getPatternCardCount()` (suffix `.count()`) still reports cards present, but the absolute `pattern-card-0` root is absent → the click waits the full 45s. Locally the whole list fits (no windowing), so `pattern-card-0` is always mounted and the bug hides — which is why it only surfaces in CI's headless viewport. ## Fix Migrate the card-root click helpers and the load-check to `patternCardAt(index)` (rendered position), consistent with `getPatternCardCount()` and the getters. Remove the now-unused absolute-index `patternCard` / `patternCardDetailsIcon` locators (the latter also misleadingly named — it resolved to the card root, not an icon). ## Validation Ran the full spec locally headless against a local **enterprise** build (v0.92.0-rc3): ``` playwright-tests/Logs/searchPatterns.spec.js — 15 passed (1.3m) ``` including the previously-failing `should open pattern details dialog when clicking pattern card`, with no regression to the other 14. Also verified via DOM probe that rendered cards are addressed correctly by rendered position while the absolute `pattern-card-0` root is the fragile anchor under windowing. ## Scope Test-only (`tests/ui-testing/pages/logsPages/logsPage.js`). No product code touched. |
||
|
|
053b1b8c09 |
test(metrics): E2E for explorer share URL and Visualize deep-link (#13747)
Adds E2E coverage for PR #13274 (metric page share URL), which shipped with unit tests but no Playwright coverage — the existing metrics-share-deep-link suite targets the legacy `/metrics/editor` route, not the Explorer. 15 tests covering: - F1 — share button presence, `metrics_data` blob written on chart build, stripped on leaving Visualize, absent on a blank canvas, volatile keys excluded, card drill-in, short-URL clipboard copy, full share round-trip - F2 — `?mode=visualize` stays on the Explorer; legacy no-mode `metrics_data` still redirects to the editor; corrupt and version-mismatched blobs absorbed New `MetricsExplorerPage` page object; registered in PageManager and the `Metrics` CI shard. Verified 15/15 on two consecutive runs against v0.92.0-rc3 (~36s). |
||
|
|
220de78fdd |
chore(deps): bump taiki-e/install-action from 2.85.5 to 2.85.10 (#13732)
Bumps [taiki-e/install-action](https://github.com/taiki-e/install-action) from 2.85.5 to 2.85.10. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's releases</a>.</em></p> <blockquote> <h2>2.85.10</h2> <ul> <li> <p>Update <code>uv@latest</code> to 0.12.2.</p> </li> <li> <p>Update <code>tombi@latest</code> to 1.2.7.</p> </li> <li> <p>Update <code>cosign@latest</code> to 3.1.3.</p> </li> <li> <p>Update <code>coreutils@latest</code> to 0.10.0.</p> </li> <li> <p>Update <code>cargo-rdme@latest</code> to 2.2.0.</p> </li> <li> <p>Update <code>cargo-crap@latest</code> to 0.4.3.</p> </li> </ul> <h2>2.85.9</h2> <ul> <li> <p>Update <code>zola@latest</code> to 0.23.1.</p> </li> <li> <p>Update <code>wild@latest</code> to 0.10.0.</p> </li> <li> <p>Update <code>mise@latest</code> to 2026.8.2.</p> </li> <li> <p>Update <code>just@latest</code> to 1.58.0.</p> </li> <li> <p>Update <code>jaq@latest</code> to 3.1.1.</p> </li> <li> <p>Update <code>cargo-nextest@latest</code> to 0.9.143.</p> </li> <li> <p>Update <code>cargo-crap@latest</code> to 0.4.2.</p> </li> <li> <p>Update <code>biome@latest</code> to 2.5.7.</p> </li> </ul> <h2>2.85.8</h2> <ul> <li> <p>Update <code>zizmor@latest</code> to 1.29.0.</p> </li> <li> <p>Update <code>typos@latest</code> to 1.49.0.</p> </li> <li> <p>Update <code>trivy@latest</code> to 0.73.0.</p> </li> <li> <p>Update <code>tombi@latest</code> to 1.2.6.</p> </li> <li> <p>Update <code>prek@latest</code> to 0.4.12.</p> </li> <li> <p>Update <code>mise@latest</code> to 2026.8.1.</p> </li> <li> <p>Update <code>convco@latest</code> to 0.7.1.</p> </li> <li> <p>Update <code>cargo-semver-checks@latest</code> to 0.50.0.</p> </li> <li> <p>Update <code>cargo-crap@latest</code> to 0.4.1.</p> </li> </ul> <h2>2.85.7</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's changelog</a>.</em></p> <blockquote> <h2>[2.85.10] - 2026-08-07</h2> <ul> <li> <p>Update <code>uv@latest</code> to 0.12.2.</p> </li> <li> <p>Update <code>tombi@latest</code> to 1.2.7.</p> </li> <li> <p>Update <code>cosign@latest</code> to 3.1.3.</p> </li> <li> <p>Update <code>coreutils@latest</code> to 0.10.0.</p> </li> <li> <p>Update <code>cargo-rdme@latest</code> to 2.2.0.</p> </li> <li> <p>Update <code>cargo-crap@latest</code> to 0.4.3.</p> </li> </ul> <h2>[2.85.9] - 2026-08-06</h2> <ul> <li> <p>Update <code>zola@latest</code> to 0.23.1.</p> </li> <li> <p>Update <code>wild@latest</code> to 0.10.0.</p> </li> <li> <p>Update <code>mise@latest</code> to 2026.8.2.</p> </li> <li> <p>Update <code>just@latest</code> to 1.58.0.</p> </li> <li> <p>Update <code>jaq@latest</code> to 3.1.1.</p> </li> <li> <p>Update <code>cargo-nextest@latest</code> to 0.9.143.</p> </li> <li> <p>Update <code>cargo-crap@latest</code> to 0.4.2.</p> </li> <li> <p>Update <code>biome@latest</code> to 2.5.7.</p> </li> </ul> <h2>[2.85.8] - 2026-08-04</h2> <ul> <li> <p>Update <code>zizmor@latest</code> to 1.29.0.</p> </li> <li> <p>Update <code>typos@latest</code> to 1.49.0.</p> </li> <li> <p>Update <code>trivy@latest</code> to 0.73.0.</p> </li> <li> <p>Update <code>tombi@latest</code> to 1.2.6.</p> </li> <li> <p>Update <code>prek@latest</code> to 0.4.12.</p> </li> <li> <p>Update <code>mise@latest</code> to 2026.8.1.</p> </li> <li> <p>Update <code>convco@latest</code> to 0.7.1.</p> </li> <li> <p>Update <code>cargo-semver-checks@latest</code> to 0.50.0.</p> </li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/taiki-e/install-action/commit/6c6fd71fe4fb72c3697d269963d0e15df8adedad"><code>6c6fd71</code></a> Release 2.85.10</li> <li><a href="https://github.com/taiki-e/install-action/commit/37cec23487191ef9aef8b1315865bd6dc584bef4"><code>37cec23</code></a> Update zola manifest</li> <li><a href="https://github.com/taiki-e/install-action/commit/4914ea4fea5759852f8cda51753420130b883d65"><code>4914ea4</code></a> Update <code>uv@latest</code> to 0.12.2</li> <li><a href="https://github.com/taiki-e/install-action/commit/0ce64163d455e35fedc5f5925221d4a71f05c990"><code>0ce6416</code></a> Update <code>tombi@latest</code> to 1.2.7</li> <li><a href="https://github.com/taiki-e/install-action/commit/1f89e2fb52c482b53fcf083bf64b5abd5d6563ab"><code>1f89e2f</code></a> Update osv-scanner manifest</li> <li><a href="https://github.com/taiki-e/install-action/commit/65ef13f21e6dd200e402682be3b1bc896f6aa862"><code>65ef13f</code></a> Update kingfisher manifest</li> <li><a href="https://github.com/taiki-e/install-action/commit/9f6a5a8ec701c59d62ac1013b92714e7410b2cae"><code>9f6a5a8</code></a> Update <code>cosign@latest</code> to 3.1.3</li> <li><a href="https://github.com/taiki-e/install-action/commit/df08c38f9c0580a749efefc712ba563ff2107db5"><code>df08c38</code></a> Update <code>coreutils@latest</code> to 0.10.0</li> <li><a href="https://github.com/taiki-e/install-action/commit/dc7bb1f807a876bf372de55372b320860efe4019"><code>dc7bb1f</code></a> Update <code>cargo-rdme@latest</code> to 2.2.0</li> <li><a href="https://github.com/taiki-e/install-action/commit/9970698e35256036b84ecfb8a70b90fe068a934b"><code>9970698</code></a> Update <code>cargo-crap@latest</code> to 0.4.3</li> <li>Additional commits viewable in <a href="https://github.com/taiki-e/install-action/compare/v2.85.5...v2.85.10">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
3886dff356 |
refactor(sentinel): dedup page-object methods + testLogger convention (follow-up to #13735) (#13746)
## Summary Post-POM-sweep cleanup (follow-up to #13727 / #13735): removes duplication and dead code introduced/surfaced during the sweep, and closes the `testLogger` convention gap. No production code — page objects + specs only. Net **−101 lines**. ## A) Duplicate method names — removed 22 shadowed dead methods 7 page objects each had **two class methods with the same name**. In JS the later definition wins, so the earlier copy was **unreachable dead code** (callers always ran the later one; tests pass with it). Removed the earlier/shadowed copy of each — behavior-preserving. `alertsPage` (5), `logsPage` (11), `pipelinesPage` (2), `metricsPage` (1), `streamsPage` (1), `cipherKeysFormValidationPage` (1), `correlationSettingsPage` (1). ## B) Duplicate implementations — consolidated - **crossLinkPage**: removed `crossLinkItemEditBtnLocatorByIdx` / `crossLinkItemDeleteBtnLocatorByIdx` — **0 callers anywhere** (incl. commented code); dead duplicates of the live `getCrossLinkEditBtnLocator` / `getCrossLinkDeleteBtnLocator`. - **alertsPage**: unified 3 identical-body pairs **via delegation** to a single impl — `getTemplateRowByText→getAlertRowByText`, `getDestinationsSelect→getAlertDestinationsSelectLocator`, `getAddAlertSubmitButton→getAlertSubmitButtonLocator`. Both names kept (zero spec edits), duplicated logic removed. ## C) Broad dead-code sweep — intentionally NOT in this PR The wider "defined-but-never-called" backlog (~890 candidates, mostly pre-existing) is a **risky, separate pass** and was deliberately excluded. ## D) testLogger — added to 22 specs missing it Added a `testLogger` import + `testLogger.testStart(testInfo.title, testInfo.file)` in `beforeEach` (or `testLogger.info` in the first test where there's no async `beforeEach`) to the 22 specs that didn't use it: 8 Correlation (5 api + 3 ui), 13 Dashboards, and `Login/auth`. Convention-only. ## Verification - **0 duplicate-name methods** remain across all page objects (was 22). - **Method-resolution scan** (every `pm.<obj>.<method>()` in all 264 specs must resolve): 4 unresolved, **all 4 pre-existing on `main`**, **0 introduced by this PR**. (This is the runtime-existence check that `node --check` can't do — the gap that caused the crossLinkPage regression on #13735.) - All changed `.js` pass `node --check`; all 22 specs have real (non-comment) `testLogger` usage. - CI-gated for browser execution. ## Stats 30 files changed (+82 / −183). |
||
|
|
bbd8601c64 |
feat(llm): add annotation queues and datasets (#13719)
## Summary - add annotation queue and dataset storage, management APIs, and review workflows - add discovery, dataset, queue, and review user interfaces - add RBAC synchronization, super-cluster support, tags, version history, and review progress ## Validation - CI should pair this branch with the identically named Enterprise branch for cumulative compilation testing ## Migration note This is an additional cumulative PR created from the committed ud/ebb3 tip. The existing SPR pull requests and branches are intentionally left unchanged. --------- Co-authored-by: sai nikhil kethe <nikhil@openobserve.ai> |
||
|
|
43b531d996 | fix: build warning v2 (#13729) | ||
|
|
801f96fc8e |
ci(reaper): label zombie runs distinctly from runner-wait (#13745)
## Problem
The Stuck-Run-Reaper conflated **two different situations** and
described both as "waiting for an EKS runner / freed a runner":
1. **runner-wait** — a job queued with no EKS runner assigned (the real
target).
2. **zombie run** — a run left in a non-completed state with **0 jobs
ever dispatched**. It never held a runner.
A real example: a **2026-05-26** run stuck ~**1842h** with 0 jobs was
cancelled and announced as *"freed a runner stuck for 1842 hours"* —
which is nonsense; nothing was ever running.
## Fix — label the two distinctly
| Case | Threshold | Wording |
|---|---|---|
| runner-wait (job queued, no runner) | **60 min** (unchanged) |
"waiting for an EKS runner that was never assigned" |
| zombie run (0 jobs dispatched) | **24h** (new
`DEFAULT_ZOMBIE_RUN_THRESHOLD_HOURS` / `zombie_run_threshold_hours`
input) | "🧟 abandoned in a non-completed state with no jobs ever
dispatched — **held no runner**, stale-run cleanup" |
- **Cancellation behaviour is unchanged** — zombies are still swept;
they're just described accurately.
- Separate longer threshold so a brief GitHub scheduling blip isn't
labelled a zombie.
- Wait time for zombies uses `run.created_at`; `humanize()` now renders
days (`76d 18h`, not `1842h 0m`).
- Job-summary table gains a **Reason** column; PR comment wording
branches per kind.
## Validation
- YAML parses; embedded `github-script` body passes `node --check`
(async-wrapped as github-script runs it).
- OSS/ENT files differ only in the `WORKFLOWS` list + label comment
(verified by diff).
- Companion PR on the same branch `test/reaper-zombie-run-labeling` in
the sister repo.
|
||
|
|
a9dce87071 |
ci(i18n): generate translations in the PR that changes en-US.json (#13741)
The translation job ran on `push: main` and opened a separate `chore/update-translations` PR. That PR had no owner — it was never assigned to or reviewed by a human — and it left a window where main carried English strings with no translations. Move the trigger to `pull_request` and commit the generated locale files back onto the PR's own head branch, so the author who added the English strings also owns their translations and both land in one merge. Notes on the mechanics: - The commit is pushed with TRANSLATIONS_APPROVE_TOKEN, not GITHUB_TOKEN. GitHub does not trigger workflows for GITHUB_TOKEN pushes, so the new head SHA would carry no status checks and required checks could never pass — the PR would be wedged. Falls back to GITHUB_TOKEN if unset. - A guard step skips the run when the head commit is already the generated translation commit, so the PAT push cannot ping-pong. - Fork PRs are skipped: their branch lives in the contributor's repo and no token here can push to it. - concurrency flips to cancel-in-progress: true. The `false` was main-specific (merges landed faster than a run finished); on a PR branch nothing is committed until a run completes and the surviving run re-derives the pending set from .translation_state.json, so a cancel costs spend, never progress. - The push retries with a rebase in case the author pushes concurrently. Cost does not regress: re-translation across branches is prevented by the committed per-key hashes in .translation_state.json, not by the trigger, so each string is still translated once — on whichever branch first introduces it. |
||
|
|
cb78ef36fd |
test(sentinel): complete POM compliance — Dashboards + Correlation (follow-up to #13727) (#13735)
## Summary Completes the Sentinel **Page Object Model** compliance sweep of the Playwright suite (follow-up to #13727, which covered the non-Dashboards shards). Every raw `page.locator(...)` / `page.getBy*(...)` selector — in **actions and assertions** — is relocated out of the spec files into the relevant page object; specs now call page-object methods/getters only. **Result: 0 raw fixture-`page` selectors across the entire `playwright-tests` suite** (single-line *and* multi-line, verified with a dotall/multiline scanner). Together with #13727, the whole suite is POM-clean. ## What's in this PR **Dashboards shards — ~1,460 selectors** relocated into their page objects (`dashboard-panel-configs/actions/edit/time`, `dashboard-chart`, `dashboard-variables-scoped`, `dashboard-filter`, `dashboard-drilldown`, `crossLinkPage`, `visualise`, `dashboard-share-export`, `dashboard-create`, `dashboard-settings`, `dashboard-variables`): | Shard | selectors | |-------|----------:| | Dashboard-Config-Settings (11 specs) | ~255 | | Dashboards-Variables (12 specs, 4 batches) | ~693 | | Dashboards-Tables (7 specs) | ~127 | | Dashboards-Charts (9 specs) | ~81 | | Dashboards-Core + Visualize (10 specs) | ~144 | | Dashboards-Settings + Panel-DateTime (8 specs) | ~107 | | Dashboard-Regression (1 spec) | ~52 | **Correlation UI specs** (3 new `Correlation/*.ui.spec.js` that arrived via main's merge, after the original audit) — converted into a new `correlationDrawerPage` + an extended `correlationSettingsPage`, with `PageManager` wired into specs that previously used none. **Audit stragglers** the original **single-line** audit grep undercounted — multi-line `page\n.locator(...)` chains in `logstable.spec.js` (→ `logsPage.getTableHeaderByField()`) and `dashboard-streaming.spec.js` (2 add-panel selectors). **AI-review fixes** (DeepSeek, on this PR) — removed 2 dead-code duplicate getters + 1 redundant method alias (merge artifacts from the sequential batch commits). One Node-context `console.log` (`dashboard-general-setting`'s `beforeEach`) → `testLogger.info`. ## Approach Nearly every Dashboards spec shares the same page objects, so the work was done in **sequential batches** (one committed, verified unit at a time) to keep page-object edits conflict-free. 13 commits, mapping 1:1 to those batches. ## Not changed (by design) - **`waitForTimeout`** left as-is — logged in the audit, not rewritten (per direction). - **Browser-context code inside `page.evaluate()` / `waitForFunction()`** (`document.querySelector`, `window.*`) left as-is — it runs in the page's JS engine, not Node, so `testLogger` (a Node module) can't be called there. *No `console.*` remained in dashboards specs*; the Node-context ones were converted to `testLogger`. - **`newPage.locator(...)`** on separate popup `Page` instances left as-is (not the fixture `page`). ## Verification - **0 raw fixture-`page` selectors** across all specs — single- and multi-line, comments stripped. - All changed `.js` files pass `node --check`. - **CI-gated** for browser execution (no local runs), consistent with #13727. The enterprise E2E crosscheck already passed against this PR. ## Stats 82 files changed (+3,355 / −2,211) — 61 specs, 21 page objects. Selectors **moved into page objects, not deleted**. |
||
|
|
25fc0baa45 |
ci(reaper): only inspect active runs, not full history (#13743)
## Problem The Stuck-Run-Reaper (merged in #13740 / #2391) hung on manual dispatch and would hit its own 10-minute job timeout **before inspecting any run**. Root cause: `listWorkflowRuns` was paginated with **no status filter**, so `github.paginate` followed every page of the workflow's **entire history** — **33,321 runs** for `playwright.yml` (~334 sequential API calls), ×8 workflows on ENT. The active set it actually needs is only ~9 runs. ## Fix Query only the non-completed statuses at the API level and de-duplicate: ```js for (const st of ['in_progress','queued','pending','waiting','requested']) { const rs = await github.paginate(listWorkflowRuns, { ...wf, status: st, per_page: 100 }); // collect, skip completed, dedupe by run id } ``` **Effect:** ~334 calls/workflow → a handful; the reaper finishes in seconds. The reaping/cancel/comment logic is unchanged. ## Validation - YAML parses; embedded `github-script` body passes `node --check` (async-wrapped as github-script runs it). - Companion PR on the same branch `test/reaper-active-runs-only` in the sister repo. |
||
|
|
11059d767b |
ci: add Playwright e2e Stuck-Run-Reaper workflow (#13740)
## What Adds a scheduled **Playwright e2e Stuck-Run-Reaper** workflow (`.github/workflows/stuck-run-reaper.yml`) that force-cancels CI runs whose jobs are stuck **waiting for a self-hosted EKS runner that is never assigned**. ## Why `timeout-minutes` (job- and step-level) only counts **execution** time — the clock starts once a job is running on a runner. It does **not** cover the time a job spends `queued` waiting for a runner to be assigned. So a job that never gets an `eks-openobserve-*` runner sits indefinitely; GitHub's only native cap for that is a very lax ~24h auto-cancel. We recently had several runs stuck 3–11h in exactly this state. This reaper closes that gap. ## How - **Trigger:** cron every 15 min + `workflow_dispatch` (with `dry_run` and `threshold_minutes` inputs). - **Runner:** `ubuntu-latest` (GitHub-hosted — deliberately not the EKS pool it polices). - **Logic:** scans non-completed runs of the policed workflows → finds jobs that are `queued`, have **no runner assigned**, request an `eks-*` label, and have waited **> 60 min** → `force-cancel`s the run. Also covers the "run never dispatched any jobs" case. - **Transparency:** on `pull_request` runs it comments on the PR explaining **which runner was never assigned, when the job was queued, how long it waited, and when it was identified as stuck**. Non-PR runs (scheduled/dispatch) are still cancelled to free the queue, with the details logged to the job summary. ## Coverage (OSS) Polices every OSS workflow using the `eks-openobserve-*` runners: | Workflow | Triggers | Reaped? | |---|---|---| | `playwright.yml` | pull_request, merge_group | ✅ (comments on PR) | | `playwright_regression.yml` | schedule, workflow_dispatch | ✅ (cancel + log) | | `release.yml` | push | ❌ excluded — stuck release builds left for a human | Job-level `timeout-minutes` in the policed workflows are intentionally left unchanged — those are correctly-sized execution caps and address a different failure mode. ### Example reaper comment > ## 🛑 Playwright run auto-cancelled by the runner-reaper > A job in this run was **waiting for a self-hosted EKS runner that was never assigned**, past the 60-minute threshold. The reaper force-cancelled the run to free the queue. > > | Job | Requested runner | Waiting since | Waited | Runner status | > |---|---|---|---|---| > | `e2e / Alerts` | `eks-openobserve-standard-8` | 2026-08-11 00:58:04 UTC | 10h 44m | ❌ never assigned | ## Notes - Safe to merge and observe: run manually via **Actions → Playwright e2e Stuck-Run-Reaper → Run workflow** with `dry_run: true` to see what it *would* cancel without acting. - Companion PR in o2-enterprise on the same branch `test/stuck-run-reaper` (broader coverage list there). |
||
|
|
82d14e8458 |
test(sentinel): POM compliance across Playwright suite (non-Dashboards) (#13727)
## What & why Sentinel (Council of Agents) audit-driven **Page Object Model compliance** across the Playwright suite. Many specs (a lot added recently, some AI-generated) had raw `page.locator(...)` / `page.getBy*(...)` selectors directly in spec files — including inside `expect(...)` — which the Sentinel flags as CRITICAL. This PR relocates those selectors into their page objects and calls page-object methods/getters instead. A full audit of **all 256 specs** was generated first (stored locally under `tests/ui-testing/MD_Files/sentinel-audit-2026-08-10/`, which is gitignored). Original totals: **1,609** raw selectors, **190** inside assertions, plus console/testLogger items. ## Scope of THIS PR (non-Dashboards) Every non-Dashboards spec is now **100% raw-selector clean** (verified: 0 occurrences). Shards covered: - **GeneralTests, Logs (Core/Builder-Advanced/Features/Regression), Traces (+Reg), Streams (+Reg), Metrics (+Reg), Pipelines, Reports (+Reg), Anomaly, Login/auth, auth-warm, and the unmatched Alerts specs.** - `console.*` → `testLogger` where it was Node-context (kept `console.*` **inside `page.evaluate()`** — that's browser context; `testLogger` doesn't exist there). - `testLogger` import/use added where missing. - **`waitForTimeout` intentionally left as-is** (logged in the audit, not rewritten — per direction). ## Deferred to a follow-up PR The **heavy Dashboards shards** (Variables 693, Config-Settings 255, Tables 127, Charts 81, Core 79, Visualize 65, Settings 52, Panel-DateTime 55, Dashboard-Regression 52 raw selectors) are **not** in this PR — they're the bulk of the work and will be a separate, focused pass. (dashboard-streaming console cleanup is included here.) ## Verification Fixes were applied and executed **shard by shard** against a local env, gated green before commit where the env allowed: | Group | Local result | |-------|--------------| | GeneralTests (edited specs) | 52 passed / 11 skipped / 0 failed | | Logs (shareLink, logsQuickPick, indexquery) | 21 passed | | Traces / Streams / Metrics | 49 passed / 2 skipped / 0 failed | | Pipelines | 56 passed / 17 skipped / 0 failed | | logs-bugs :548, dashboard-streaming | verified not regressed (isolation) | **Alerts create-wizard specs** (Alerts shard, Alerts-Regression, unmatched Alerts) could not be gated locally — the alert-creation wizard's stream popover times out on this env **on unmodified code too** (reproduced on original, test `:363`), so those refactors are 1:1 selector→method mappings, syntax-clean (`node --check`), and rely on CI to gate. Every changed `.js` passes `node --check`. `waitForTimeout` unchanged; no behavior changes intended. ## Stats 51 files changed. Selectors moved into page objects, not deleted. |
||
|
|
ca75da8b78 |
fix(db): scope postgres catalog lookups to the active schema (#13723)
Fixes #11342. ## Problem Upgrading fails on any deployment using a non-`public` postgres schema: ``` [POSTGRES] Fresh install: creating partitioned table file_list db init failed: SqlxError# relation "file_list" already exists ``` The DDL and DML we emit are **unqualified** and resolve through `search_path`, but the catalog / `information_schema` lookups that decide *what* to emit were pinned to a literal `public`. The two disagree: `get_table_relkind` reports "table missing" for a table that plainly exists, we take the fresh-install branch, and `CREATE TABLE` collides with the table already sitting in the user's schema. **The panic is not the worst case.** When a stale same-named table exists in `public` (old deployment, another tenant), the probe returns `'r'` and we take the *regular-to-partitioned migration* branch against the wrong schema's table — renaming its indexes. That matches the index-name conflicts the reporter described. The reporter found 2 sites; there were 14 in total. ## Approach Replace the literal `'public'` with `current_schema()` inline in each catalog query. It evaluates on the very connection that runs the query — the first existing entry of that connection's `search_path`, i.e. exactly where its unqualified DDL/DML lands — so a probe can never disagree with the writes again. No Rust-side state. Two details: - `pg_class` lookups join `pg_namespace` on `nspname = current_schema()` instead of casting to `regnamespace`: the cast parses its input as an identifier and fails on schema names with special characters (e.g. `My-Schema.X`). - `verify_schema_consistency()` at startup checks the read-write, read-only and DDL pools resolve to the same schema. They come from three separately configured DSNs, and `ZO_META_DDL_DSN` is documented as a different **role**, which can carry its own `search_path` via `ALTER ROLE ... SET search_path` — e.g. `cache_indices` reads on `CLIENT` while `create_index` writes on `CLIENT_DDL`, so a divergence would silently relocate the same bug class. Fail loudly instead. ## Sites fixed | File | Functions | |---|---| | `file_list/postgres.rs` | `get_table_relkind`, `check_partition_exists`, `drop_empty_partitions`, `get_varchar_column_width`, `apply_autovacuum_tuning`, `align_identity_sequence` | | `db/postgres.rs` | `cache_indices` (had **no** schema filter at all), `add_column`, `drop_column`, meta `start_dt` probe | | `migration/adapter/postgres.rs` | `list_tables`, `get_columns`, `get_foreign_keys`, `sync_sequences` | | 2 sea-orm migrations | postgres branch had no schema filter, while the mysql branch correctly used `DATABASE()` | The migration-adapter ones matter independently: under a non-public schema `list_tables()` returned **zero** rows, so the cross-database migration tool reported success having migrated nothing. ## Verification Against a real PostgreSQL 18.4 instance with the app configured with `search_path=o2`: - **Fresh install** into schema `o2`: boots clean, all 121 tables land in `o2`. - **Forced upgrade path** (`db_schema_version` lowered) **with a stale regular `public.file_list` planted**: logs `Table file_list already partitioned, ensuring partitions`, upgrade completes, and the stale `public` table is untouched. On main this exact setup either panics (`relation "file_list" already exists`) or migrates the wrong schema's table. - Every rewritten query shape additionally verified in psql under schema `o2test` and under a schema named `My-Schema.X` (stale-table isolation, partition listing, varchar width, identity-sequence alignment, index cache, adapter `list_tables`). `cargo check --bin openobserve`, `cargo fmt --check`, `cargo clippy -p infra --all-targets` all clean; 108 infra postgres unit tests pass. ## Not covered here 1. **Recovery for already-broken databases.** This prevents the failure but does not unstick installs that are mid-migration with renamed indexes. Worth a follow-up (idempotent cleanup, or a documented recovery procedure). 2. **CI coverage.** There is no job exercising a non-`public` `search_path`, which is why these 14 sites accumulated across different changes. A postgres job with `search_path=o2test` would stop this recurring. |
||
|
|
b3e6a77270 |
fix(correlation): service-streams P2 behavioral fixes + discovered services flat table (#13710)
Correlation P2 round: behavioral/security fixes across the service-streams backend and correlation FE, plus the Discovered Services UX rework and the correlation e2e suites. **Companion enterprise PR (must merge together):** openobserve/o2-enterprise#2381 — the enterprise side calls the new 3-arg `put(org, record, max_streams_per_type)` introduced here. ## Commit 1 — behavioral fixes (F2–F38) **Backend** - F25/F26: RBAC on `list_services` (`service_streams` GET — the one previously-unenforced endpoint in the module) + validate `distinguish_by` ids on identity-config save - F2: validate trace filter param before WHERE-clause interpolation - F6: `_reset` invalidates caches on all nodes via reload event; org deletion clears service-streams caches - F8: shared coverage-based identity-set ranking for ingest and read - F10: purge rows of removed/changed identity sets on config save - F19: `put` returns deleted orphan keys for cache eviction - F28: discriminate no-match from failure; stop aliasing 404 to no-match - F32: cap DB-side stream union at `max_streams_per_type` - `all_dimensions` unions on put so late-tracked dimensions surface - F14: `StreamInfo.dropped_dimensions` contract field; allow empty `tracked_alias_ids` when identity sets exist **Frontend** - F35/F36/F31: per-stream filter-edit resolution via semantic groups; dimension edits in both key spaces; subject overrides never guess fields - F27/F30: echo `source_stream`/`source_type`; stop guessing another stream's filters; chip dedup - F2/F38: SQL field/value escaping via shared `quoteSqlLiteral` helper ## Commit 2 — Discovered Services UX + e2e - Flat table (one row per instance), same-name services merge their name cell (OTable `pivotRowColumns`); optional per-service collapse with aggregated summary row; search/filters override collapse - Fixes an OTable pivot-merge bug (value-keyed merge map blanked single-column runs) + regression test - Semantic Field Groups: category tabs with counts + cross-category search with match highlighting - Correlation Phase-1 API + Phase-2 UI Playwright suites ## ⚠️ Release note (enterprise + RBAC deployments) Viewing Discovered Services now requires the **Service Streams** permission (previously unenforced). Root/admin users are unaffected; custom roles may need a one-time grant via the role editor (resource already exists in the OFGA model). ## Checks - `cargo fmt --all --check` ✅ · `cargo clippy --all-targets` ✅ enterprise + OSS feature sets (0 warnings) - prettier ✅ · eslint 0 errors · 207 unit tests on touched specs ✅ - Compiles with paired enterprise branch ✅ |
||
|
|
a2f950153e |
fix(logs): pass the expanded row to View Trace (#13722)
#13708 ## Problem Clicking **View Trace** inside an expanded log row on the Logs page does nothing and throws: ``` TypeError: Cannot read properties of undefined (reading '_timestamp') at na (SearchResult.B_xPyti5.js:7:22212) at De (JsonPreview.CX_tRWWx.js:2:9172) ``` The same button in the row-detail drawer works, so the failure reads as intermittent. ## Root cause `JsonPreview` emitted `view-trace` with no payload, while the result grid's expansion slot bound the handler by reference: ```vue <template #expansion="{ row }"> <JsonPreview :value="row" … @view-trace="redirectToTraces" /> </template> ``` `redirectToTraces(log)` then read `log[store.state.zoConfig.timestamp_column]` off `undefined` — and `_timestamp` is that column's default value, which is exactly the reported error. The binding used to work because the table component in between re-emitted the event with the row attached (`emits("view-trace", row)`). That component was removed when the logs result grid migrated from `TenstackTable` to `OTable`, and the payload-less binding was carried over unchanged. The row-detail drawer resolves the hit in its own template, which is why only the inline expansion broke. The correlation drawer's log table (`CorrelatedLogsTable`) had the identical defect — not reported, same fix. ## Fix - `JsonPreview` emits the previewed log with the event, matching its sibling `show-correlation` emit, so every current and future listener receives the record. - Both expansion slots pass `row` explicitly, restoring the pre-migration semantics at the call site. - Both handlers no-op with a warning toast when the record is missing, so a visible, enabled button can never again be a silent dead end during triage. No change to when the button is shown, to the ±15 min window, or to the traces route contract. ## Tests Three regression specs, all failing before the fix with the exact `TypeError` above: - `JsonPreview.spec.ts` — the existing test only asserted *that* `view-trace` fired, never with what; it now asserts the payload, which is why the regression slipped through. - `SearchResult.viewTrace.spec.ts` (new) — expanded row emits a payload-less `view-trace`; asserts navigation to `traceDetails` with the right `trace_id`, `span_id`, and time window. - `CorrelatedLogsTable.viewTrace.spec.ts` (new) — same for the correlation drawer. Both new files are focused specs because neither existing suite installs a router or renders `OTable`'s expansion slot. ``` Test Files 8 passed (8) Tests 316 passed | 3 skipped (319) ``` `npm run type-check` → clean. `eslint` on the touched files → 0 errors. ## Verification Logs page with a trace-id-bearing stream and `service_streams_enabled` off: expand a row inline → **View Trace** lands on the trace with the right id and window. Same from the row-detail drawer and the correlation drawer's log table; console clean in all three. |
||
|
|
7ab93c04d5 |
test(reports): heal reports-regression timezone spec (edit-prefill race + toast selector) (#13724)
## What Heals the Reports regression spec `reports-regression-bugs.spec.js` → *"Report save should not shift timestamp by timezone offset"* (@bug-11231), which was failing deterministically (all 4 retries) in both the OSS and ENT **Playwright Regression** runs: - OSS: https://github.com/openobserve/openobserve/actions/runs/31364820545 - ENT: https://github.com/openobserve/o2-enterprise/actions/runs/31364817885 This is **test-only** — the product is unaffected (the historical #11231 fix and normal report save both work; a save actually succeeds in the failing run). ## Root cause (two independent defects) 1. **Wrong success-toast selector (deterministic line-118 failure).** The save assertion used `page.getByRole('alert')`. `OToast` renders the **success** variant with `role="status"` — only **error/warning** toasts get `role="alert"` ([`OToast.vue`](https://github.com/openobserve/openobserve/blob/main/web/src/lib/feedback/Toast/OToast.vue) `rootRole`). So the assertion could never match a successful save and timed out. → now asserts on the success toast `[data-test-variant="success"]`, consistent with the rest of the file. 2. **Edit-prefill race (line-84 failure, the one captured in the trace).** On the edit flow, step-1 `Continue` could fire before the async edit prefill (`form.reset()` after the report GET) populated folder/dashboard/tabs. `goToStep()` then failed validation and silently stayed on step 1, so the schedule toggle never rendered and the test timed out looking for it. → `createReportContinueButtonStep1()` now retries the click until the schedule step is actually reached, **keeping** the original post-advance settle so downstream step-2 interactions (schedule-later toggle, step-2 Continue) don't race. Also dedups the spec's inline date/time entry through the existing `createReportDateTime()` helper (logic unchanged). ## Verification (local, `localhost:5080`) | Suite | Result | |---|---| | `reports-regression-bugs.spec.js` (target) | ✅ stable across repeated runs (1 + 3× repeat-each) | | `reportsScheduleLater.spec.js` | ✅ 6/6 | | `reportsScheduleNow.spec.js` + `reportCsvMediaType.spec.js` | ✅ 13/13 | The two changed page-object methods are shared with the sibling Reports specs above; all pass, confirming no regression. ## Scope The spec lives in OSS `tests/` and is tree-merged into the ENT regression run, so this single OSS change fixes both pipelines. No ENT repo change required; the ENT regression will be re-triggered against this branch. |
||
|
|
bf494ce1de |
fix(synthetics): move check variables into a shared resizable panel (#13699)
## What
Check variables lived inside the **Auth & Network** card on the
Configure page, so an author writing `{{token}}` references into journey
steps could not see them while editing. They now live in a
**`CheckVariablesPanel`** mounted as the right-hand pane on **both**
wizard pages (Journey + Configure), over one source of truth —
`check.variables`, **no schema change**.
## Panel behaviour
- **Cards** — name, masked value for secrets (raw value never rendered),
and a usage count computed from exact `{{name}}` matches across each
step's `value`, `selector`, and locator candidates. The "Used in N
steps" line only appears when the variable is actually referenced.
- **Add / edit** — inline, one form at a time, with the existing
validation rules (`^[A-Za-z_][A-Za-z0-9_]*$`, no duplicates). The add
form appends to the end of the list and scrolls into view; its button
stays pinned below the scroll region so it never scrolls away.
Re-pressing it while the form is open re-scrolls instead of discarding a
half-typed draft.
- **Delete** — confirmed via `ODialog`, and the dialog says what's at
stake: *"baseUrl will be removed from this check. It is referenced by 1
step, which will no longer resolve."* Still backed by the ~6s undo that
restores at the original index.
- **Empty state** — `OEmptyState` whose action is the sole Add
affordance while the list is empty.
- **Layout** — resizable via `OSplitter`; width is shared across both
pages through a new `useCheckWizardUi` composable. The Journey toolbar
gets a collapse toggle beside **Record**, following the field-list
pattern in `logs/SearchResult.vue`.
`CheckAuthNetwork` loses its Variables sub-section; its header summary
is now basic-auth only.
## Two OStepper library fixes this depended on
`OStep` is a multi-root component, so a `class` passed to it was
**silently dropped**. Every `h-full` below it then resolved against an
indefinite height — which left the splitter separator **0px tall and
impossible to grab**. (It predates this branch; step 1 already carried a
no-op `h-full!`.)
- `OStep` now forwards attrs explicitly — onto the **whole step** in
vertical mode, where they used to auto-inherit, and onto the content
panel in horizontal mode.
- `OStepper`'s horizontal pane gets `min-h-0` so a height-constrained
stepper clamps its content instead of growing with it (without it the
Configure separator measured 1818px and the panel was not truly pinned).
Verified the other horizontal-stepper consumers
(`CreateDestinationForm`, `OrgStorageEditor`) and the vertical
`BrowserJourneyStepEditor` are unaffected.
## Testing
**Automated** — 1147 tests pass across `components/synthetics`,
`views/synthetics`, `lib/navigation`, and `composables/synthetics`,
including a new 26-test `CheckVariablesPanel.spec.ts`
(add/edit/validation/masking/usage counts/scroll-to-form/confirm+undo
with fake timers). `type-check:app`, `lint:design:strict`, and ESLint
are clean — ESLint warning count is identical to baseline (0 errors).
**Manual, in the running app** — full flow driven end to end: gate →
journey → add with an invalid name rejected → valid add → splitter drag
(separator now 467px tall, drag moves it) → collapse/reopen preserving
the dragged width → step referencing `{{baseUrl}}` flipping the usage
badge to 1 → delete confirmation warning about the broken step → cancel
keeps it → confirm + undo restores it → Configure page showing the same
panel at the same width with the Auth & Network card carrying no
Variables section.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
a338597481 |
fix(lib): re-place toggle pill after an item reorder (#13721)
The sliding selection pill in OToggleGroup was re-measured only on a modelValue change or a track resize. A drag-to-reorder moves the active item but leaves the track exactly the same size, so neither trigger fired: the pill kept the previous item's position and width, landing under the wrong item and overlapping its neighbour, while the active text colour — being data-state driven — correctly followed the moved item. On the traces detail tab bar that read as the highlight and the colour coming apart. Observe the track's childList and snap-measure on a change, mirroring OTabs, which already did this for its underline. Attributes are deliberately not observed: data-state flips on a real selection change, which the modelValue watcher handles with a slide, and snapping there would cancel it. Also covers items being added or removed, e.g. the traces Thread tab appearing for a trace with LLM spans. |
||
|
|
087e1498ab |
feat(slos): add SLO burn-rate alerts (#13704)
design at /slo ## What Adds **SLO burn-rate alerts** — an alert family that fires off an SLO's error budget rather than a raw query — and finishes the SLO surface around it: navigation entry, detail-page fixes, and the flag removal now that SLOs have shipped. ## Changes **SLO burn-rate alerts (backend)** - New burn-rate evaluation (`config/src/meta/slo/burn.rs`, `condition.rs`) with multi-window burn conditions, and `core/src/slo/evaluate.rs` extended to evaluate them on the SLO job. - Alerts carry an `slo_id`; `infra/src/table/alerts` gains the column, filtering, and query support so an SLO's alerts can be listed and deleted with it. - Alert levels (`meta/alerts/level.rs`) and tags extended for the SLO family; scheduler handlers route SLO alerts through the same notification path. **SLO alert UI** - `SloAlertForm` + `AlertDetailSlo`: create, edit and view burn-rate alerts from the SLO detail page and the alert list. - Payload/routing helpers (`utils/alerts/sloAlertPayload.ts`, `sloAlertRouting.ts`, `alertPayload.ts`) with unit tests. - `AlertDestinationsField` extracted as the complete Destination form field — side label, tooltip, combined destinations/workflows picker, inline error, refresh, and create-in-new-tab. It replaces two verbatim copies in the generic alert form and is reused by the SLO form and synthetics `CheckAlerts` (which sets `supportsWorkflows: false`, since a check has no workflow routing). Fixes the SLO form's previously unwired refresh and Add Destination buttons. **SLO detail page fixes** - Burndown and burn-rate charts drew as bare axes when coverage was sparse: `showSymbol: false` means an isolated measured bucket between gaps has no segment and paints nothing. Symbols are now sized per point, and a sparse-coverage note states how many buckets actually recorded events. - Nothing scrolled below ~700px of viewport height — `OPageLayout`'s fixed flex body plus `OTabPanels` `scroll="none"` clipped the panels. Panels now grow and scroll; the stat strip and tab bar stay pinned. Verified at 1024x500, 1280x620, 1512x945. **Other** - `ZO_SLO_ENABLED` removed — SLOs have shipped, so the flag and its status/route gating are gone. - SLOs surfaced in the main nav. - Dashboard chart panel header gets its own tint via new `--color-panel-bar-bg` / `--color-panel-bar-border` tokens, and the bar is extracted into `PanelBar.vue` (it existed as five hand-copied class strings that had already drifted). - `tsconfig.vitest.tsbuildinfo` untracked; third-party vendor name removed from comments and test names. ## Testing - New Rust tests across `meta/slo/burn.rs`, `condition.rs`, `core/src/alerts/alert.rs` and `infra/src/table/alerts`. - New frontend suites: `sloAlertPayload.spec.ts`, `sloAlertRouting.spec.ts`, `alertPayload.spec.ts`, `AlertDetailSlo.spec.ts`, `SloDetailAlerts.spec.ts`, `SloListDelete.spec.ts`, `SloTimeSlicePreview.spec.ts`. - `cargo clippy --all-targets` and `web` `lint:ci` clean. |
||
|
|
d93d599918 |
chore: update translations from en-US.json (#13694)
Auto-generated translation updates from the English source file (`web/src/locales/languages/en-US.json`). 🤖 Generated with automated translation workflow --------- Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com> Co-authored-by: ktx-vaidehi <vaidehi.akhani@kiara.tech> |
||
|
|
dd5ff2c131 |
feat(iam): serve the MCP Server setup page on the OSS build (#13720)
## Summary IAM > MCP Server was gated to enterprise/cloud, but the endpoint it documents is not. The backend registers api-endpoint outside the ent block and it initialises the MCP tools "for all editions" — only the OAuth *discovery* endpoints are enterprise-only. OSS users could reach the MCP server, just not the page that tells them how. Removes the build/runtime gates so the page ships everywhere, and hides the one part that genuinely is enterprise-only. ### Gates removed - `useEnterpriseRoutes.ts`: the `mcpServer` child was pushed inside the `isCloud || isEnterprise` branch, so on OSS the route did not exist at all. Moved into the base `children` array. - `IdentityAccessManagement.vue`: the tab's `visible` was `isEnt && zoConfig.ai_enabled`. Dropped. `ai_enabled` is `enterprise_value!(false, o2cfg.ai.enabled)`, i.e. always false on OSS, so that condition could never have let the tab through. - `useIngestionRoutes.ts` / `Recommended.vue`: the Ingestion > Recommended cross-link carried the same gate, explicitly to match where `mcpServer` existed. Un-gated to stay consistent. ### Card corrections - Drop the "MCP server is an Enterprise feature." note and delete the now-dead `ingestion.mcp.enterpriseNote` key from all 15 locale files. - OAuth *is* enterprise-only: `oauth_authorization_server_metadata` and `oauth_protected_resource_metadata` are `cfg(not(enterprise))` 404 stubs. The OAuth tab is now hidden on OSS and `authMode` defaults to `token`, rather than landing users on a flow that cannot complete. With one method left the tab picker is hidden too. The token path works unchanged on OSS: the snippets authenticate with `Basic base64(email:passcode)`. "Generate read-only credential" stays hidden there — `useMcpCredential` requires `rbac_enabled` to attach the read-only role — which degrades cleanly to the quick-start passcode and produces identical snippets. ### Tests Updated the OSS IAM child-count assertion (5 -> 6), the only test the change broke. Added OSS-presence coverage in `useEnterpriseRoutes.spec`, `useIngestionRoutes.spec`, `IdentityAccessManagement.spec` (with `ai_enabled` off) and `Recommended.spec`. Verified with `format:check`, `verify` (type-check:app + 5 lint gates, 0 errors) and 457 tests across the 7 affected specs. Refs #13717 |
||
|
|
e1aad76283 |
fix(dashboards): use bookmark-add for the panel annotation button (#13718)
The panel header's Add Annotation toggle rendered the same 'edit' glyph that the kebab menu uses for Edit Panel, so one shape stood for two unrelated actions on the same panel. Annotations are markLines pinned to a point on the x-axis, which bookmark-add states without borrowing the pencil. |
||
|
|
9cf4933f15 | fix: Browser sdk test 0.4.2 (#13584) | ||
|
|
62528b4c12 |
fix(synthetics): sequential recorder setup checklist with state-safe refresh and incognito failure recovery (#13684)
## Summary Follow-up to #13658 — redesigns the recorder setup flow as a sequential-gated checklist and closes the UX holes around refresh, stale connections, and incognito failures. ### Sequential-gated setup checklist (shared by the create-page setup phase and the journey dialog) - Only one task is actionable at a time; later tasks render locked, done tasks collapse to a green check row. - **Task 1 (install)**: Web Store button + an "I've added it to Chrome" attestation (the install happens in another tab the page can't observe). Live bridge detection auto-completes it ("Detected automatically"); checkbox-only completion offers Undo. - **Task 2 (incognito)**: "Do this in Chrome" callout with inline Chrome-UI icons + first-person attestation, collapsing to "Allowed in Incognito" with Undo. - **Task 3 (connect)**: completes only on the real probe signal — attestations can't skip into a recording that would hang. Icon-click is the primary instruction (injects without reloading); refresh is the secondary link. - Journey dialog: per-action subtitle, "n of 3 done" badge, full-width CTA reading "<action> — finish the tasks above first" while locked, a hint naming the exact blocking task, skip link (record only). - Attestations are session-only by design — persisting them would keep tasks pre-completed after the extension is removed. ### State-safe refresh + gate bypass - Entering the setup phase mirrors url/name into the query (`setup=1`); mount restores them, so a mid-setup F5 returns to the checklist with fields intact. - The checklist's "refresh this page" strips the setup flag; when url+name are present and the warm probe detects the extension, the gate auto-commits straight into the editor (recording stays a deliberate click). `?url=&name=` deep links work as a byproduct. ### Incognito failure recovery - Both the replay preflight classification and the recording-start refusal (the extension's raw "Recording needs incognito access…" message) now revoke the disproved attestation and auto-open the setup dialog on its incognito task, with the failed action as the CTA. The warning card slims to Retry / Dismiss / Show setup steps (its chrome://extensions walkthrough duplicated the checklist, and `window.open("chrome://…")` is blocked by Chrome anyway). ### Compulsory re-verification after the incognito toggle - Toggling "Allow in Incognito" **reloads the extension and orphans the tab's bridge** (per the takeover-handshake contract in the extension's content script), so giving the ack invalidates `extensionReady` and re-probes — the connect task stays open until a fresh probe passes. The install fact is latched first so task 1 doesn't regress. ## Test plan - 141 tests across the four specs: ExtensionSetupChecklist 18, ExtensionSetupDialog 19, BrowserJourney 60, CreateBrowserTest 44 — including the deferred-probe assertions that the CTA follows the *fresh* probe verdict, not stale state - `npm run lint` / `npm run type-check` / `npm run lint:design:strict` clean - Manual: full install → incognito → connect flow against the published extension, both failure paths (record + replay without incognito), refresh at each task |
||
|
|
93ee50ca17 |
docs: fix occured typo in alert and folder doc comments (#13705)
`occured` should be `occurred`. Four occurrences, all in rustdoc comments on error variants: - `src/core/src/alerts/alert.rs:235,240` - `src/db/src/folders.rs:83,88` No identifiers, error messages or code paths change. The remaining two hits in the tree are in `src/config/src/utils/async_walkdir/`, which is a vendored copy of the `async_walkdir` crate and carries its upstream copyright headers, so I left those untouched. |
||
|
|
4d23d85fcb | fix(i18n): improve Simplified Chinese translations (#13707) | ||
|
|
575e8ea4d5 |
feat: add zstd compression on OTLP gRPC ingest (#13703)
Design at: #13702 / inline in PR description Add the `zstd` feature to tonic and enable `accept_compressed(Zstd)` on the OTLP logs, metrics and trace gRPC services in both `run_common` and run_router`. Closes #13702 ## Out of scope - Response compression on `send_compressed(Zstd)` - OTLP/HTTP path (tower-http zstd features) - snappy would be the better choice under CPU pressure, but tonic has no snappy support. |
||
|
|
f0e12bb913 |
fix(traces): scope "View Logs" correlated query to the clicked span (#13700)
## Problem In enterprise, the span sidebar's **View Logs** goes through `navigateToCorrelatedLogs()`, which built its WHERE clause purely from the per-stream `filters` returned by `_correlate`. The result was a service/namespace-wide log query with no reference to the span the user actually clicked. The OSS path (`buildQueryDetails`) pins both `span_id` and `trace_id`. The enterprise path did not. ## Change Append `span_id` and `trace_id` conditions in `navigateToCorrelatedLogs()`: - **Field names** from the org's `span_id_field_name` / `trace_id_field_name` settings - **Values** from `searchObj.data.traceDetails.selectedSpanId` and `selectedTrace?.trace_id` — the same sources `buildQueryDetails` reads, and `selectedSpanId` is what drives the sidebar's `:span` prop, so it always matches the clicked span - Each condition is missing-value guarded and routed through the existing semantic-group keying, so an id field that is also a tracked dimension yields one condition rather than two — with the exact id winning over a stream's alias ## Deduplication The new code would otherwise have been a third copy of logic already present twice, so: - Extracted `getSpanIdField()` / `getTraceIdField()`; `buildQueryDetails` and `navigateToCorrelatedLogs` now share them. This also fixes a latent bug — `buildQueryDetails` did `String(spanIdField)` with no fallback, emitting a column literally named `undefined` when the org setting was absent. Defaults now mirror `stores/index.ts`. - Extracted `groupIdFor()` / `setCondition()` so the stream-filter loop and the id loop share one implementation. - Dropped the `usedGroups` Set — every `add()` was paired with a `conditions.set()` on the same key, making `usedGroups.has(x)` always equivalent to `conditions.has(x)`. - Replaced the hand-rolled `String(v).replace(/'/g, "''")` with `quoteSqlLiteral()` from `utils/telemetryCorrelation` (byte-identical output). Deliberately *not* `buildSqlCondition()` — it wraps identifiers via `quoteSqlIdentifier` (always quotes), which would change emitted SQL from `field = 'v'` to `"field" = 'v'`. |
||
|
|
8303be0b4c |
refactor: rename AlertManager node role to Scheduler (#13696)
## Summary - rename the `AlertManager` node role to `Scheduler` across Rust services, jobs, protobuf bindings, UI labels, and tests - emit `scheduler` as the canonical role name - continue accepting legacy `alertmanager` and `alert_manager` configuration values - keep protobuf enum value `5` unchanged for wire compatibility ## Why The role schedules and executes alerts, reports, pipelines, synthetics, and other background work. `Scheduler` describes that broader responsibility more accurately. ## Compatibility - existing role configuration values remain accepted through parser and serde aliases - the protobuf numeric value remains stable - coordinated changes are included in the Enterprise, Helm chart, and documentation repositories ## Verification - `cargo test -p config test_role` - `cargo test -p openobserve-node` - `cargo check -p openobserve-jobs` - targeted web tests: 307 passed, 13 skipped - Rust formatting and targeted Prettier checks |
||
|
|
735dc7d58d |
feat(traces): restore in-page Service Graph / Service Catalog tabs alongside standalone routes (#13698)
## What #13659 converted Service Graph and Services Catalog from in-page tabs on the Traces page into standalone routes reached from the nav-rail flyout. This PR restores the in-page tabs as a **second access path** — both now coexist, with distinct semantics: | Path | Semantics | |---|---| | Toolbar tabs on `/traces` | In-page view switch (`?tab=service-graph` / `?tab=services-catalog`), shares the page's stream + time context — the pre-#13659 UX, including old `?tab=` bookmarks | | Nav-rail flyout | Navigates to the standalone routes `/traces/service-graph`, `/traces/services` — unchanged from #13659 | Both paths render the same inner components (`ServiceGraph.vue`, `ServicesCatalog.vue`); only toolbar chrome differs. ## How A guided **reverse-apply of #13659's diff to `Index.vue` + `SearchBar.vue` only** (verified byte-identical to the pre-PR files — both were untouched on main since), with four deliberate adaptations: - **Re-added `applyHandoffFilter`** + its two hook call sites, which the revert would have deleted — the standalone pages' "View Traces" handoff (`?filter=` landing) depends on it. Covered by a new unit regression test and verified live. - The restored in-page `view-traces` handlers call `viewTracesHandoff.ts` (`buildViewTracesFilter` / `normalizeViewTracesPayload`) instead of resurrecting the inline filter builders that #13659 extracted from them — no re-duplication. - **Fixed a latent pre-#13659 bug**: the graph tab's refresh button called `serviceGraphRef?.loadServiceGraph()`, which was never in `ServiceGraph.vue`'s `expose()` — now `refresh()`. - **Flyout highlight now follows the visible view** on both paths: new `SubnavChild.activeOnTab` alias marks the Service Graph / Service Catalog flyout items active when `/traces?tab=…` shows their view in-page. Previously the same view highlighted different flyout items depending on how you reached it — the only submenu that deviated. Also: - Renamed the "Services" submenu / page header / route title to **"Service Catalog"**, matching the toolbar tab label (single `menu.services` locale value covers all). - `global-setup.js` login verification moved to `navbar-main-nav` — the same stale-selector fix #13659 applied to `enhanced-baseFixtures.js`; suites fail setup on main without it. ## Tests | Suite | Result | |---|---| | Unit: `SearchBar` 69, `Index` 76, `ONavGroup` 18 (5 new `activeOnTab` cases), `viewTracesHandoff` 21, `navGroups` 28 | all pass (233 total, 2 pre-existing skips) | | E2E (new) `Traces/traces-toolbar-tabs.spec.js` — in-page tab switches, shared-datetime carry, `?tab=` bookmark reload, flyout→standalone-route coexistence | 4/4 vs live enterprise build | | E2E `Traces/service-catalog.spec.js` (standalone-route regression) | 18 pass | | `type-check`, `eslint` (0 errors), `prettier`, `lint:design:strict` | clean | Not run: `Traces/service-graph.spec.js` — its `beforeAll` ingests via `ZO_BASE_URL`, which requires a single-origin instance (CI); the dev vite+backend split can't satisfy it. Nothing in this PR touches `ServiceGraph.vue` or the standalone views. ## Notes for review - `Index.vue` / `SearchBar.vue` restored regions are intentionally verbatim pre-#13659 (including its 16 pre-existing lint *warnings*) — deviations are limited to the four adaptations above, so the diff-vs-pre-PR is auditable. - Two known-inert quirks restored deliberately, not bugs to fix here: `hide-splitter-separator` has no CSS rule (was a no-op pre-#13659 too), and `@update:activeTab` writes to a getter-only computed. --------- Co-authored-by: Shrinath Rao <shnath@openobserve.ai> |
||
|
|
47d510aa16 |
feat: rebuild the RUM error detail page around issue impact (#13697)
Design at: designs/mobile-rum-sdk-evaluation/design-docs The page showed a single error event: header, tag strip, stack trace, replay button, event list. It could not say how bad the error was, who it reached, whether it was new, or where it concentrated — it never queried anything beyond that one row. It is now a two-column triage surface: the diagnosis path (occurrences chart, stack trace, breadcrumbs) on the left, corroborating evidence (replay, backend trace, breakdown, who hit it) on the right. - ErrorHeader: error type as the title with handling/source badges, the message in an alert banner, and a context line carrying event id, route and the service/version/env chips. Copy-permalink and copy-event-id actions. - ErrorImpactStrip: occurrences, users, sessions, first/last seen. A dash, not a zero, when a figure is unavailable. - ErrorOccurrencesChart: this signature over time, marking the bucket that holds the event the page was opened on. - ErrorFacetBreakdown: browser / OS / release / page as share-of-occurrences bars, calling out any value that owns 80%+ of the traffic — usually the fix. - ErrorContextCard: identity, device, geo and deployment for this occurrence. Supersedes ErrorTags, which is removed. Backed by useErrorDetail + errorDetailQueries: an impact aggregate gates a histogram and two facet group-bys. Three searches, staged inside the server's work-group cap and aborted when the view is left. Facets are split across two group-bys on purpose — one query over all four columns returns their cross product. Also fixes the breadcrumb trail: it read an hour of the whole stream with no session filter, so "what the user did before the crash" could be other people's clicks. It is now scoped to the session id. |
||
|
|
6c43e01823 | fix: px to rem (#13541) | ||
|
|
eb2ab67f5a | fix: dashboard table column formatting via column header (#13695) |