Skip to content

fix(api/history): honor provider/account_ids/start/end query params in Purchase History handler - #716

Merged
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/701-history-filter-params
May 25, 2026
Merged

cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/701-history-filter-params

Conversation

@cristim

@cristim cristim commented May 25, 2026

Copy link
Copy Markdown
Member

Closes #701. Backend fetchPurchaseHistory was silently ignoring provider, account_ids, start, and end query params; visible UI filters were no-ops.

Changes

  • New historyFilters struct in internal/api/handler_history.go:
  • Both halves of the merged response (DB rows + synthesized executions) apply the same filter set.
  • Store: added store_postgres query branches that append AND provider = $? AND cloud_account_id = ANY($?) AND scheduled_date BETWEEN $? AND $? only when the corresponding filter is set.

Tests (12 new + 4 pgxmock)

  • TestHandler_getHistory_FilterParams × 6: provider alone, provider=all, account_ids alone, legacy account_id ignored when new filters present, legacy fast-path, start/end alone, combined.
  • TestHandler_getHistory_FilterValidation × 6: invalid provider, non-UUID account, malformed start/end, inverted range, >366 days.
  • TestHandler_getHistory_DateRangeBoundary × 2: 366 accepted, 367 rejected.
  • TestParseHistoryDateRange × 4: empty/end-only/start-only/inclusive end.
  • store_postgres_pgxmock_test.go × 4: all filters, no filters, partial, limit clamp.

Scope

Closes #701 only. Issue #503 (Savings History chart re-query) requires a separate frontend wiring fix in frontend/src/modules/savings-history.ts — different layer; bundling would expand scope.

Verification

  • go test across api/config/mocks/scheduler/server/purchase/analytics: 2530 pass
  • gofmt + go vet + pre-commit hooks clean
  • 11 files changed, +918/-20 lines

…n Purchase History handler

The /api/history handler accepted provider, account_ids, start, and end
query params from the frontend (Purchase History date filter row 3.4 and
the Purchases-page Global filter rows 4.2/4.4/4.5) but silently dropped
them: fetchPurchaseHistory only forwarded account_id and limit to the
store, and fetchExecutionsAsHistory ignored every filter. Visible filter
affordances were no-ops.

Parse the four query params in a shared historyFilters struct so both
halves of the merged /api/history response (the SQL purchase_history rows
and the synthesised execution rows) apply the same scope:

  - provider validated via the existing whitelist (aws/azure/gcp/"all"/"")
  - account_ids parsed via parseAccountIDs (UUIDs, MaxAccountIDsPerRequest)
  - start/end as YYYY-MM-DD with a 366-day cap mirroring PR #529 (issue
    #414) to prevent a full-table-scan DoS

New store method GetPurchaseHistoryFiltered pushes the filter set into a
WHERE clause built like buildRecommendationFilter; each predicate is
applied only when its filter is non-empty. The legacy GetPurchaseHistory /
GetAllPurchaseHistory methods are preserved for the no-filter fast path
(dashboard, inventory, analytics) and for the legacy singular account_id
param, which targets a different (VARCHAR(20)) column than the
cloud_account_id UUIDs the new filter consumes and must not be coerced
into the new filter's clause.

Tests cover each filter independently and combined, the legacy
account_id fast path, malformed/inverted/oversized dates returning 400,
the 366-day boundary (366 accepted, 367 rejected), and the pgxmock SQL
shape for the new store method including the no-filter / partial-filter /
limit-clamp variants.

Closes #701
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/many Affects most users effort/m Days type/bug Defect labels May 25, 2026
@coderabbitai

coderabbitai Bot commented May 25, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 25 minutes and 46 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf4f4515-50ab-498c-bd1a-523887aee70b

📥 Commits

Reviewing files that changed from the base of the PR and between e7103d8 and f123ba1.

📒 Files selected for processing (11)
  • internal/analytics/collector_test.go
  • internal/api/handler_history.go
  • internal/api/handler_history_test.go
  • internal/api/mocks_test.go
  • internal/config/interfaces.go
  • internal/config/store_postgres.go
  • internal/config/store_postgres_pgxmock_test.go
  • internal/mocks/stores.go
  • internal/purchase/mocks_test.go
  • internal/scheduler/scheduler_test.go
  • internal/server/test_helpers_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/701-history-filter-params

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

@cristim

cristim commented May 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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 May 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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 May 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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 May 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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 May 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

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 merged commit fd33b50 into feat/multicloud-web-frontend May 25, 2026
4 checks passed
@cristim
cristim deleted the fix/701-history-filter-params branch May 25, 2026 22:08
cristim added a commit that referenced this pull request May 27, 2026
…mers (refs #701) (#741)

* fix(purchases): wire topbar filter chips to all three Purchases consumers (#701)

Three sub-flows of the global filter on the Purchases page still failed
after PR #716:

1. Provider filter (4.2): selecting a provider did nothing to Purchase
   History or Approval Queue because setupHistoryHandlers() was exported
   from history.ts but never called in app.ts.

2. Account filter (4.4): the Savings History chart disappeared when an
   account UUID was selected because the analytics SQL only matched the
   legacy account_id VARCHAR(20) column, not cloud_account_id UUID FK.
   The topbar chip always sends cloud_accounts.id (a UUID), so the WHERE
   clause returned 0 rows and the chart fell into the empty-state branch.

3. Empty-state UX (4.4/4.5): showEmptyState() always showed "No savings
   history data available yet." regardless of whether a filter caused the
   empty result, giving no signal to the user.

Changes:
- frontend/src/app.ts: import and call setupHistoryHandlers() after
  initSavingsHistory() so provider/account chip changes reload Purchase
  History and Approval Queue.
- internal/api/analytics_postgres.go: add OR cloud_account_id::text = $3
  to the WHERE clause in QueryHistory and QueryBreakdown so UUID-based
  account filtering includes rows written by current purchase flows.
- frontend/src/modules/savings-history.ts: add buildFilterDesc() helper
  and update showEmptyState() to show "No savings data for the selected
  filter (AWS)" when a filter is active vs the original message when not.
- Tests: new Go tests for UUID account filter path; new TS tests for
  setupHistoryHandlers subscriptions and filter-aware empty-state copy;
  update history mocks in index/sidebar-anchors/purchase-execution-toast
  tests to include setupHistoryHandlers.

* fix(savings-history): address CR findings on PR #741

Three CodeRabbit findings addressed:

1. (Major, line 67) Use explicit error state on fetch failure: add
   showErrorState() that sets distinct heading/help copy ("Failed to
   load savings history. / Please retry.") instead of the no-data
   empty state, so users can tell a filter-scope empty from a real
   outage. Update the catch path to call showErrorState with the
   extracted error message.

2. (Minor, line 125) Treat the 'all' provider sentinel as unfiltered
   in buildFilterDesc: skip pushing provider.toUpperCase() when the
   value is 'all' (case-insensitive), so the empty-state heading reads
   "No savings history data available yet." rather than "No savings
   data for the selected filter (ALL)." when no real provider chip is
   selected.

3. (Minor, line 184) Tighten SQL expectations in
   TestQueryHistory_CloudAccountIDFilter and
   TestQueryBreakdown_CloudAccountIDFilter: prefix regexes with (?s)
   and extend to include the dual-column account filter WHERE fragment,
   so removing that clause would now cause the mock expectations to
   fail instead of passing silently.

Tests: all 66 savings-history frontend tests pass; Go analytics tests
pass (including the two tightened CloudAccountID tests).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/many Affects most users priority/p1 Next up; this sprint 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