fix(analytics): gate the per-tenant metric filter behind config (default off) - #2117
Conversation
…ult off) constructorfabric#1967 replaced the compiler's shared-WHERE no-op with a hard `tenant_id = <session tenant>` on every observation and cohort read. The ingested `tenant_id` in the bronze/silver/gold pipeline is not yet aligned to the JWT tenant (the alignment is constructorfabric#1829), so the exact match silently empties every metric read — existing metrics return no rows, no error. Gate the injection behind `metric_catalog.enforce_tenant_scope`, defaulting to `false` (no tenant isolation in the data today). When off, each read swaps the exact-match term for a tautology that still binds the same one placeholder, so param arity is identical in both modes — the pre-constructorfabric#1967 behavior. Flip the flag on per environment once the ingest tenant is aligned (constructorfabric#1829). The handler sets the flag from config; the compiler helper `tenant_predicate` selects the term. Enforcement stays unit-tested (fixtures pin it on); a new test covers the bypass form and asserts placeholder arity is unchanged. Refs constructorfabric#1967, constructorfabric#1829 Signed-off-by: Anton Zelenov <antonz@constructor.tech>
📝 WalkthroughWalkthroughMetric result requests now receive tenant-scope enforcement from metric catalog configuration. Query compilation applies exact or bypass tenant predicates across metric and peer queries while preserving SQL parameter ordering. Fixtures and tests cover both enforcement modes. ChangesMetric tenant-scope control
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant MetricResultsAPI
participant MetricCatalogConfig
participant MetricResultsCompiler
participant SQLQuery
MetricResultsAPI->>MetricCatalogConfig: Read enforce_tenant_scope
MetricResultsAPI->>MetricResultsCompiler: Pass validated request
MetricResultsCompiler->>SQLQuery: Build tenant-scoped metric and peer predicates
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/backend/services/analytics/src/config.rs (1)
88-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove source comments that duplicate rollout context.
Move environment-variable instructions and rollout rationale to configuration documentation. Remove issue references from source. Keep only a concise
// INVARIANT:comment when code and a specification-named test cannot express the rule.
src/backend/services/analytics/src/config.rs#L88-L94: move the field rationale and environment-variable path out of the service source.src/backend/services/analytics/src/domain/metric_results/validation.rs#L41-L43: remove the field documentation comment.src/backend/services/analytics/src/domain/metric_results/validation.rs#L186-L188: remove the initialization comment.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L511-L512: remove the parameter-order comment.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L754-L755: remove the private-helper documentation comment.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L837-L845: reduce this to a concise invariant without issue references.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L850-L852: remove the bypass comment.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L1253-L1255: remove the test implementation comment.src/backend/services/analytics/src/domain/metric_results/compiler.rs#L1268-L1268: remove the test implementation comment.As per coding guidelines, “Use comments only when code cannot express the reason” and keep issue context in issues or PRs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/services/analytics/src/config.rs` around lines 88 - 94, Remove rollout rationale, environment-variable guidance, issue references, and implementation comments at src/backend/services/analytics/src/config.rs:88-94, src/backend/services/analytics/src/domain/metric_results/validation.rs:41-43 and 186-188, and src/backend/services/analytics/src/domain/metric_results/compiler.rs:511-512, 754-755, 850-852, 1253-1255, and 1268-1268; move configuration guidance to documentation. At compiler.rs:837-845, retain only a concise // INVARIANT: comment expressing the rule without issue references.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/backend/services/analytics/src/config.rs`:
- Around line 88-94: Remove rollout rationale, environment-variable guidance,
issue references, and implementation comments at
src/backend/services/analytics/src/config.rs:88-94,
src/backend/services/analytics/src/domain/metric_results/validation.rs:41-43 and
186-188, and
src/backend/services/analytics/src/domain/metric_results/compiler.rs:511-512,
754-755, 850-852, 1253-1255, and 1268-1268; move configuration guidance to
documentation. At compiler.rs:837-845, retain only a concise // INVARIANT:
comment expressing the rule without issue references.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 19ba34fd-4597-4f98-8c6d-1e00ff69fd9d
📒 Files selected for processing (6)
src/backend/services/analytics/src/api/metric_results.rssrc/backend/services/analytics/src/config.rssrc/backend/services/analytics/src/domain/metric_results/batch.rssrc/backend/services/analytics/src/domain/metric_results/builder.rssrc/backend/services/analytics/src/domain/metric_results/compiler.rssrc/backend/services/analytics/src/domain/metric_results/validation.rs
Problem
#1967 replaced the metric compiler's shared-
WHEREno-op with a hardtenant_id = <session tenant>on every observation and cohort read (metric_where,shared_observation_where, and the peer-cohort CTEs). It bindsctx.subject_tenant_id()— the JWT tenant.But the observation
tenant_idis a connector-stampedNullable(String)carried verbatim bronze →class_*→ gold; it is not yet aligned to the JWT tenant representation (that alignment is #1829). So the exact match now filters out every row: existing metrics return no data, with no error. Before #1967 the no-op masked this.Fix
Gate the injection behind a new
metric_catalog.enforce_tenant_scopeflag, defaulting tofalse— there is no tenant isolation in the bronze/silver/gold data today, so enforcing here only empties reads.tenant_predicate(enforce)helper:true→tenant_id = ?(the [pres] Flat tenant data-row filter #1967 behavior)false→(tenant_id = ? OR 1 = 1)— a tautology that still binds the same one placeholder, so param arity is identical in both modes (no bind-order risk); this is the pre-[pres] Flat tenant data-row filter #1967 behavior.APP__gears__analytics__config__metric_catalog__enforce_tenant_scope=true.Flip the flag on per environment once the ingest tenant is aligned to the JWT
tid(#1829).Tests
cargo test -p analyticsgreen (533 passing). Enforcement stays covered — the compiler fixtures pinenforce_tenant_scope: true, so the existing "tenant predicate leads every read" assertions are unchanged. A new test covers the bypass form and asserts placeholder arity is unchanged in both modes.cargo clippy -p analytics --all-targetsclean.Notes
cpt-presentation-component-metric-compiler/ the isolation NFR) becomes config-gated. I'll reconcile that wording in the presentation spec once the in-flight [pres] Contract version stamp #1969/[pres] Query console FE #1970 spec PRs land, to avoid three branches editingDESIGN.mdconcurrently.Refs #1967, #1829
Summary by CodeRabbit