Skip to content

fix(dashboard): apply provider filter to savings-history chart (closes #498) - #1122

Merged
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/498-savings-provider-filter
Jun 9, 2026
Merged

fix(dashboard): apply provider filter to savings-history chart (closes #498)#1122
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/498-savings-provider-filter

Conversation

@cristim

@cristim cristim commented Jun 9, 2026

Copy link
Copy Markdown
Member

Summary

The dashboard Savings History / savings-over-time chart (and the YTD KPI sparkline) ignored the global provider filter: selecting AWS / Azure / GCP showed the same data regardless of which provider was selected. Account filtering (fixed in #956) worked; provider filtering on this chart did not.

Fixes the NEW bug reported under issue #498, QA row 2.3.

Root cause

The chart is backed by GET /history/analytics. The handler getHistoryAnalytics read and applied the account_id filter but never read params["provider"], and PostgresAnalyticsClient.QueryHistory had no provider WHERE predicate at all. The frontend modules/savings-history.ts forwarded provider (commented as a "no-op until #502 lands"), while the Home dashboard.ts trend chart deliberately dropped it. Net effect: the savings-history query always aggregated all providers.

By contrast, the working charts (/history, and the trends QueryByService) read provider, normalise the "all" sentinel to "", validate it, and apply AND provider = $N.

Fix (mirrors the existing provider-filter pattern)

  • handler_analytics.go getHistoryAnalytics: read provider, normalise "all" -> "" (no filter), validateProvider at the boundary, pass it to QueryHistory.
  • analytics_postgres.go QueryHistory + AnalyticsClientInterface: add a provider param and an optional parameter-bound AND provider = $N fragment ("" = all providers), positioned after the dual-column account binds.
  • dashboard.ts loadSavingsTrendChart: forward the provider chip and name it in the empty-state (now accurate, since the query is provider-scoped). modules/savings-history.ts: corrected the stale comment (already forwarded the param).
  • No silent fallbacks: empty / "all" provider preserves the existing "all providers" semantics intentionally; an unknown provider returns a 400.

Repro (QA row 2.3)

  1. Have purchase history across more than one provider.
  2. Open the Home dashboard; note the Savings-over-time chart and YTD sparkline.
  3. Select AWS in the global provider filter, then Azure / GCP.
  4. Before: the chart shows identical data for every provider. After: the chart is scoped to the selected provider's rows only.

Tests (fail pre-fix, pass post-fix)

  • TestQueryHistory_ProviderFilter, TestQueryHistory_ProviderFilterWithAccount — assert the AND provider = $N bind and its position.
  • TestHandler_getHistoryAnalytics_ProviderFilter, _ProviderAllIsNoFilter, _InvalidProvider — threading, the "all" sentinel, boundary rejection.
  • dashboard.test.ts — asserts the provider chip is forwarded to the API (and omitted when none selected); empty-state names the provider.

Verified pre-fix failure / post-fix pass for the core regressions. go build ./..., go test ./internal/api/... ./internal/analytics/..., frontend tsc + the affected jest suites all green.

Closes #498

Summary by CodeRabbit

  • New Features

    • Savings trend analytics now respect the provider filter selection, ensuring data displayed matches the active filter.
    • Empty-state messages are now context-aware, displaying which provider and accounts are currently filtered.
  • Tests

    • Extended test coverage for provider-filtered analytics behavior and empty-state scenarios.

…498)

The Savings History / savings-over-time chart and YTD KPI sparkline are
backed by GET /history/analytics. The handler read and applied the
account filter but never read params["provider"], and QueryHistory had
no provider predicate, so the series showed identical all-provider data
regardless of the selected AWS/Azure/GCP chip (QA row 2.3). The Home
dashboard trend chart also deliberately dropped the provider param when
calling the endpoint.

Thread the provider through end to end, mirroring the working
/history and trends (QueryByService) paths:

- handler getHistoryAnalytics: read provider, normalise the "all"
  sentinel to "" (no filter) and validateProvider at the boundary,
  then pass it to QueryHistory.
- PostgresAnalyticsClient.QueryHistory + AnalyticsClientInterface: add a
  provider param and an optional parameter-bound `AND provider = $N`
  WHERE fragment ("" = all providers), positioned after the dual-column
  account binds.
- dashboard.ts loadSavingsTrendChart: forward the provider chip (and
  name it in the empty-state via buildFilterDesc, now that the query is
  actually provider-scoped). modules/savings-history.ts already
  forwarded provider; correct its stale "no-op until #502" comment.

Regression tests fail pre-fix and pass post-fix:
- TestQueryHistory_ProviderFilter / _ProviderFilterWithAccount assert the
  provider bind and its position.
- TestHandler_getHistoryAnalytics_ProviderFilter / _ProviderAllIsNoFilter
  / _InvalidProvider cover threading, the "all" sentinel, and boundary
  rejection.
- dashboard.test.ts asserts the provider chip is forwarded to the API and
  omitted when no provider is selected.

Also reset the topbar-filter mocks in the savings-trend describe block so
tests stay isolated now that the chart reads the provider chip.
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c43fba65-d632-4f2b-bbc8-7cf6edeb12ca

📥 Commits

Reviewing files that changed from the base of the PR and between 31d83e7 and 9730a99.

📒 Files selected for processing (10)
  • frontend/src/__tests__/dashboard.test.ts
  • frontend/src/dashboard.ts
  • frontend/src/modules/savings-history.ts
  • internal/api/analytics_postgres.go
  • internal/api/analytics_postgres_test.go
  • internal/api/handler_analytics.go
  • internal/api/handler_analytics_test.go
  • internal/api/handler_coverage_test.go
  • internal/api/handler_per_account_perms_test.go
  • internal/api/types.go

📝 Walkthrough

Walkthrough

Provider filter from the dashboard is now forwarded through the analytics API to the backend, where it is validated and applied as a SQL WHERE clause in history analytics queries. Frontend empty-state copy reflects the active provider filter.

Changes

Provider filtering in savings analytics

Layer / File(s) Summary
Interface contract: QueryHistory signature
internal/api/types.go
AnalyticsClientInterface.QueryHistory gains a provider string parameter between account filters and time window parameters.
Frontend provider integration and empty-state
frontend/src/dashboard.ts, frontend/src/modules/savings-history.ts, frontend/src/__tests__/dashboard.test.ts
loadSavingsTrendChart() reads active provider from filter state and passes it to getSavingsAnalytics; empty-state copy is built from combined provider + account filter description. Test setup resets mocks to prevent filter leakage; new tests verify provider parameter is passed when active and omitted when inactive; empty-state message includes provider name when filter is active.
Handler validation and forwarding
internal/api/handler_analytics.go, internal/api/handler_analytics_test.go
Handler normalizes provider filter (converts "all" to "") and validates via validateProvider before forwarding to QueryHistory. Tests verify "aws" passes through unchanged, "all" is normalized to empty string, and invalid providers prevent QueryHistory invocation.
Storage: provider WHERE clause
internal/api/analytics_postgres.go
QueryHistory adds optional provider filter clause to SQL WHERE when provider is non-empty, conditionally appending AND provider = $N alongside account predicates.
Storage-level provider filter tests
internal/api/analytics_postgres_test.go
New TestQueryHistory_ProviderFilter asserts provider-only filtering generates correct SQL; existing filter tests updated to use new signature. Tests verify provider parameter binding and interaction with account filters.
Handler and coverage test updates
internal/api/handler_analytics_test.go, internal/api/handler_coverage_test.go, internal/api/handler_per_account_perms_test.go
Handler mock expectations updated to include provider parameter; coverage and per-account-perms tests updated to match new QueryHistory call signature.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • LeanerCloud/CUDly#498: This PR implements the QA reference step 2.3 mentioned in the issue — provider filtering is now applied to the savings analytics query at the backend level, addressing the "provider filter is similarly unreliable" caveat in the original report.

Possibly related PRs

  • LeanerCloud/CUDly#747: Both update dashboard.ts and dashboard.test.ts to forward active filter state when reloading savings-trend analytics; PR #747 wired account filtering, this PR extends it to include provider filtering.
  • LeanerCloud/CUDly#766: Both modify loadSavingsTrendChart() flow in dashboard.ts and how the savings-trend chart responds to filter changes.
  • LeanerCloud/CUDly#956: Both update analytics_postgres.go and handler_analytics.go to extend QueryHistory filtering; PR #956 added dual account-id matching, this PR further adds provider predicate.

Suggested labels

effort/m

Poem

🐰 The provider now flows through the pipes,
From dashboard chip to SQL stripes,
WHERE clauses bind what once was free,
Analytics scoped, as filters decree! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: applying provider filter to savings-history chart, referencing the closed issue.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #498: provider filter is now properly forwarded to analytics queries, backend applies provider WHERE filter, frontend displays provider-aware empty states, and unit/integration tests verify provider parameter threading.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #498 requirements: backend provider filter logic, frontend provider forwarding, test updates for new signature, and internal comment clarifications remain in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 fix/498-savings-provider-filter

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

@cristim cristim added bug Something isn't working triaged Item has been triaged priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/many Affects most users effort/s Hours type/bug Defect labels Jun 9, 2026
@cristim

cristim commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim
cristim merged commit 61788c1 into feat/multicloud-web-frontend Jun 9, 2026
5 checks passed
@cristim
cristim deleted the fix/498-savings-provider-filter branch July 27, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working effort/s Hours impact/many Affects most users priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant