Skip to content

feat(analytics): named run params — inject {tenant}, bind {period} (#1966) - #2078

Merged
cyberantonz merged 2 commits into
mainfrom
pres/1966-query-params
Jul 31, 2026
Merged

feat(analytics): named run params — inject {tenant}, bind {period} (#1966)#2078
cyberantonz merged 2 commits into
mainfrom
pres/1966-query-params

Conversation

@cyberantonz

@cyberantonz cyberantonz commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

Adds named query parameters to the saved-query run path (Phase A, presentation layer).

POST /v1/queries/{id}/run now binds ClickHouse server-side parameters:

  • {tenant} — always injected from the session SecurityContext; never client-settable.
  • {period} — optional, supplied on the run body { "period": "<value>" }.

Values are passed through ClickHouse's param_<name> interface (Query::param), so a parameter value can never alter query structure — no string interpolation. The single-SELECT gate already tolerates {name:Type} placeholders, so authored SQL like SELECT ... WHERE insight_tenant_id = {tenant:UUID} AND ts >= {period:Date} passes on write and run. A query that references an unbound named parameter (e.g. {period} with no period supplied) is surfaced as a 400, not a bare 500.

The run request body is optional (Option<Json<..>>), so existing param-less runs are unchanged.

Scope boundary

This binds the {tenant} value. The injected tenant-row filter (insight_tenant_id = {tenant} added to the compiler's shared WHERE) remains #1967.

Specs

Registered cfs artifacts updated: cpt-presentation-fr-query-params marked shipped; run endpoint contract in DESIGN §3.3 and the saved-query-api component updated. cfs validate green on both PRD and DESIGN. OpenAPI contract regenerated (new RunSavedQueryRequest schema + optional request body).

Tests

  • Offline unit: classify_run_error (456 → 400, others → 500) and RunSavedQueryRequest deserializer (absent/{}/value).
  • Live e2e (test_queries.py): {tenant} injection returns the signed session tenant, {period} binding echoes the supplied value, missing-param → 400.
  • cargo test -p analytics (487 passed), clippy + fmt clean, OpenAPI drift gate passes.

Closes #1966
Part of #1803

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Saved queries can now accept an optional period value when run.
    • Tenant context is automatically applied; clients cannot override it.
    • Requests may omit the JSON body when no parameters are needed.
    • Missing required query parameters now return a clear 400 error.
  • Documentation

    • Updated API specifications and examples to describe named parameter support and validation behavior.

…1966)

/v1/queries/{id}/run now binds ClickHouse server-side parameters: {tenant}
is always injected from the session context (never client-settable), and an
optional `period` on the run body binds {period}. Values pass through the
param interface, so a value can never alter query structure; the single-SELECT
gate already tolerates {name:Type} placeholders. A query that references an
unbound named parameter is surfaced as a 400, not a bare 500.

PRD/DESIGN: cpt-presentation-fr-query-params marked shipped; run endpoint
contract + OpenAPI regenerated. Coverage: offline classifier + deserializer
unit tests, live e2e for tenant injection, period binding, and missing-param.

Closes #1966
Part of #1803

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
@cyberantonz
cyberantonz requested a review from a team as a code owner July 30, 2026 14:57
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Regenerate the connectors-ddl snapshot

This PR changes src/ingestion/**. If your change affects any
bronze / silver / gold schema, regenerate the committed DDL snapshot
and include it in this PR.

Prerequisites (details: src/ingestion/scripts/bootstrap-db/README.md):

  • docker + a fresh throwaway ClickHouse 25.7.5 (README "Local ClickHouse for testing")
  • .env from .env.bootstrap.example pointing at it; use the host LAN IP,
    reachable from both the host and connector containers
    (host.docker.internal does not resolve on the macOS host itself)
  • python3.12 or python3.11 on PATH (pinned dbt venv)
  • HubSpot + Salesforce credentials in .env — their discover calls the
    live APIs; without them, apply ../connectors-ddl/{hubspot,salesforce}.sql
    (relative to bootstrap-db/) to seed their bronze, then run the dbt step
cd src/ingestion/scripts/bootstrap-db
set -a; source pins.env; source .env; set +a
./bootstrap-db.sh connectors-config.yaml   # fresh ClickHouse 25.7.5
./dump-ddl.sh                              # writes scripts/connectors-ddl/*.sql

Commit the resulting scripts/connectors-ddl/*.sql diff. If nothing
changed, no snapshot update is needed. (Regeneration is manual for now.)

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cyberantonz, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e4c812f-296b-492e-8308-d5219c215dd1

📥 Commits

Reviewing files that changed from the base of the PR and between e967ae7 and 47e455f.

📒 Files selected for processing (2)
  • docs/domain/presentation-layer/specs/DESIGN.md
  • src/backend/services/analytics/src/api/saved_queries.rs
📝 Walkthrough

Walkthrough

The saved-query run endpoint now accepts an optional period, always binds tenant from request context, uses ClickHouse server-side parameters, classifies missing parameters as HTTP 400, and documents and tests the updated behavior.

Changes

Saved-query named parameters

Layer / File(s) Summary
Request contract and optional body
src/backend/services/analytics/src/domain/saved_query.rs, src/backend/services/analytics/src/api/mod.rs, docs/components/backend/analytics/openapi.json, docs/domain/presentation-layer/specs/*
Defines RunSavedQueryRequest with optional period, permits an absent JSON body, and documents tenant and period binding semantics.
ClickHouse binding and error classification
src/backend/services/analytics/src/api/saved_queries.rs
Binds tenant and period through ClickHouse parameters and maps missing period errors to invalid-argument responses while preserving internal errors for other failures.
Named parameter contract tests
src/ingestion/tests/e2e/api/test_queries.py
Tests tenant injection, period binding, and missing-period HTTP 400 behavior with cleanup of created queries.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AnalyticsAPI
  participant SecurityContext
  participant ClickHouse
  Client->>AnalyticsAPI: POST /v1/queries/{id}/run with optional period
  AnalyticsAPI->>SecurityContext: Read tenant
  AnalyticsAPI->>ClickHouse: Execute query with tenant and period parameters
  ClickHouse-->>AnalyticsAPI: Rows or missing-parameter error
  AnalyticsAPI-->>Client: JSON rows or HTTP 400/500 response
Loading

Possibly related PRs

Suggested reviewers: aleksdotbar

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: named run params with tenant injection and period binding.
Linked Issues check ✅ Passed The changes implement #1966 by always injecting {tenant} and adding {period} on the saved-query run path.
Out of Scope Changes check ✅ Passed The additional docs, OpenAPI, tests, and error handling directly support the named-parameter feature and stay in scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pres/1966-query-params

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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

Inline comments:
In `@docs/domain/presentation-layer/specs/DESIGN.md`:
- Around line 361-363: Update the POST /v1/queries/{id}/run documentation to
state that tenant-row filtering is deferred to `#1967` rather than already
injected, while preserving the existing description of tenant context binding
and the explicit no-predicate-yet wording elsewhere.

In `@src/backend/services/analytics/src/api/saved_queries.rs`:
- Around line 211-218: Update the UNKNOWN_QUERY_PARAMETER handling in the
SavedQueryError mapping to extract the actually unbound parameter name and
report that name in the field violation. Use period only when the missing
parameter is period; otherwise return the generic/SQL contract violation or
reject unsupported placeholders during saving, while preserving the existing
handling for Code: 456.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 27adc37b-e2fe-4922-960b-7da6c3bcd687

📥 Commits

Reviewing files that changed from the base of the PR and between 99697d1 and e967ae7.

📒 Files selected for processing (7)
  • docs/components/backend/analytics/openapi.json
  • docs/domain/presentation-layer/specs/DESIGN.md
  • docs/domain/presentation-layer/specs/PRD.md
  • src/backend/services/analytics/src/api/mod.rs
  • src/backend/services/analytics/src/api/saved_queries.rs
  • src/backend/services/analytics/src/domain/saved_query.rs
  • src/ingestion/tests/e2e/api/test_queries.py

Comment thread docs/domain/presentation-layer/specs/DESIGN.md Outdated
Comment thread src/backend/services/analytics/src/api/saved_queries.rs Outdated
…r defer (#1966)

CodeRabbit review on #2078:

- classify_run_error named every UNKNOWN_QUERY_PARAMETER as missing `period`;
  a query using `{region}` was mislabeled. Extract the backtick-quoted name
  from the ClickHouse message and report that, falling back to a generic
  `params` violation when the server does not name it.
- DESIGN §3.3 run row read as if the tenant-row filter were already injected;
  say the filter is deferred to #1967 (the value is bound, no predicate yet),
  matching the boundary note.

Part of #1803
Refs #1966

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Anton Zelenov <antonz@constructor.tech>
@cyberantonz
cyberantonz added this pull request to the merge queue Jul 31, 2026
Merged via the queue into main with commit 20294e6 Jul 31, 2026
45 of 46 checks passed
@cyberantonz
cyberantonz deleted the pres/1966-query-params branch July 31, 2026 00:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[pres] Query named params (tenant + period)

2 participants