Skip to content

fix(frontend/api): preserve details + engine on purchase POST (closes #597) - #600

Merged
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-597-frontend-details
May 20, 2026
Merged

fix(frontend/api): preserve details + engine on purchase POST (closes #597)#600
cristim merged 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-597-frontend-details

Conversation

@cristim

@cristim cristim commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

The dashboard purchase flow was silently dropping the details (and engine, cloud_account_id) fields from every recommendation before POSTing to /api/purchases/execute. Both handleExecutePurchase and handleFanOutExecute in app.ts reconstructed the recommendation object field-by-field, leaving details out. The backend's DecodeServiceDetailsFor received nil and fell back to zero-valued defaults: Platform=Linux/UNIX, Tenancy=default, Scope=Region. Windows EC2, dedicated-tenancy, and AZ-scoped RIs mis-purchased as Linux/regional/default; non-default-engine RDS/Cache rows failed with "no offerings found for Linux/UNIX default". Only Linux/regional/shared recs accidentally worked. The details field itself was missing from both the Recommendation API interface and the LocalRecommendation front-end type, making the omission invisible to TypeScript.

What changed

  • frontend/src/api/types.ts: Added details?: unknown to Recommendation interface. The field is typed as opaque unknown (not a strict union) because the frontend treats it as a pass-through blob matching the backend's json.RawMessage field.
  • frontend/src/types.ts: Added details?: unknown to LocalRecommendation interface so the field is preserved in the local cache from the GET /api/recommendations response.
  • frontend/src/app.ts: Replaced the field-by-field map() reconstructions in both handleExecutePurchase (single-rec path) and handleFanOutExecute (fan-out path) with spread syntax: { ...r, monthly_cost: r.monthly_cost ?? null, payment: ..., selected: true, purchased: false }. All server-provided fields including details, engine, cloud_account_id, and any future additions now flow through unchanged.
  • frontend/src/__tests__/purchase-execution-toast.test.ts: Added 4 regression tests asserting executePurchase is called with details (and engine) preserved for an EC2 Windows fixture and an RDS Postgres fixture on both the single-rec and fan-out paths.

Why it matters

This is a P0 silent mis-purchase bug affecting every dashboard purchase that is NOT Linux/regional/shared-tenancy. Windows EC2, dedicated-tenancy RIs, AZ-scoped RIs, and non-default-engine RDS/ElastiCache rows were either purchasing the wrong configuration or failing outright. The backend codec fix in #454 completed the server-side round-trip but the frontend was never sending details in the first place.

Test plan

  • npm test -- --testPathPattern=purchase-execution-toast - all 15 tests pass (11 existing + 4 new)
  • Full test suite: npm test --no-coverage - 1902 tests, 0 failures
  • TypeScript: tsc --noEmit - 0 errors
  • Manual: open dashboard Network tab, select a Windows EC2 or RDS recommendation, click purchase, verify POST body includes details key
  • Manual: confirm Windows EC2 / dedicated-tenancy / AZ-scoped RI purchases succeed end-to-end

Closes #597
Refs #453, #454

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Ensured additional recommendation details are properly preserved and forwarded during purchase execution operations.
  • Tests

    • Added comprehensive test coverage for purchase execution detail handling across all execution paths.

Review Change Stack

…597)

Both handleExecutePurchase and handleFanOutExecute reconstructed the
recommendation object field-by-field before POSTing to /api/purchases/execute,
silently dropping details, engine, and cloud_account_id. The backend's
DecodeServiceDetailsFor received nil Details, fell back to zero-valued
ComputeDetails, and substituted Platform=Linux/UNIX, Tenancy=default,
Scope=Region for every purchase regardless of the actual recommendation.

Windows EC2, dedicated-tenancy, and AZ-scoped RIs mis-purchased as
Linux/regional/default; non-default-engine RDS/Cache rows failed with
"no offerings found". Only Linux/regional/shared recs accidentally worked.

Fix:
- Add details?: unknown to Recommendation (api/types.ts) and
  LocalRecommendation (types.ts) as an opaque pass-through blob matching
  the backend's json.RawMessage field (added in #454).
- Replace both field-by-field map() reconstructions in app.ts with
  spread syntax: { ...r, payment: ..., selected: true, purchased: false }.
  All server-provided fields (details, engine, cloud_account_id, and any
  future additions) now flow through unchanged.
- Add 4 regression tests in purchase-execution-toast.test.ts asserting
  that executePurchase receives details and engine for an EC2 Windows
  fixture and an RDS Postgres fixture on both the single-rec and fan-out
  paths.

Refs #453, #454
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e150bc1f-ce12-4327-afe8-be1e7de8d4dc

📥 Commits

Reviewing files that changed from the base of the PR and between bc8831b and ecbfc3f.

📒 Files selected for processing (4)
  • frontend/src/__tests__/purchase-execution-toast.test.ts
  • frontend/src/api/types.ts
  • frontend/src/app.ts
  • frontend/src/types.ts

📝 Walkthrough

Walkthrough

This PR fixes a critical bug where the frontend's Recommendation type omitted the details field, causing all purchase executions to lose per-recommendation service details. Frontend now preserves and forwards details via object spread in purchase payloads, backed by comprehensive test coverage for both single-record and fan-out execution paths.

Changes

Details field preservation in purchase execution

Layer / File(s) Summary
Type contracts for details field
frontend/src/api/types.ts, frontend/src/types.ts
Recommendation and LocalRecommendation interfaces now include optional details?: unknown field, documented as an opaque backend-provided ServiceDetails payload to be forwarded unchanged during purchase execution and absent on older cached rows.
Purchase execution payload construction
frontend/src/app.ts
Single-bucket and fan-out purchase execution flows now spread the entire recommendation object (...r) to forward details, engine, cloud_account_id, and other fields unchanged while overriding only payment, monthly_cost, and canonical selected/purchased flags.
Test coverage for details preservation
frontend/src/__tests__/purchase-execution-toast.test.ts
Four new tests verify details are preserved in the POST body: two single-record path tests for EC2 Windows and RDS Postgres recommendations, and two fan-out path tests for fan-out EC2 and RDS recommendations including engine preservation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

  • #453: The frontend changes to preserve and forward the recommendation.details field in the executePurchase payload are directly related to the backend issue about persisting and decoding service Details for purchase execution — they operate on the same Details field and complement each other.

Possibly related PRs

  • LeanerCloud/CUDly#454: The main PR's frontend changes and test coverage to preserve and round-trip Recommendation.details in the executePurchase request align directly with PR #454's backend work to persist RecommendationRecord.Details and decode it into the correct typed ServiceDetails during purchase execution.
  • LeanerCloud/CUDly#501: Both PRs are about preserving the opaque details/ServiceDetails payload through the purchase execution flow—main PR ensures details is forwarded in the frontend api.executePurchase POST body, while retrieved PR adds a backend regression test that Details is correctly decoded into typed SavingsPlanDetails for a legacy savings-plans slug.
  • LeanerCloud/CUDly#193: Both PRs modify the purchase-execution request payload construction in frontend/src/app.ts::handleExecutePurchase and fan-out path so that fields from the UI/local recommendation object are forwarded—main PR preserves details by spreading the whole recommendation, while the retrieved PR ensures per-row payment is mapped from r.payment.

Poem

A rabbit hops through TypeScript fields,
Spreading whole recommendations—details revealed! 🐰
Windows and Postgres now fly true,
No more Linux defaults for me and you.
Details preserved from end to end,
Purchase execution's trusted friend! ✨

🚥 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 accurately describes the main fix: preserving the details and engine fields on purchase POST requests to resolve issue #597.
Linked Issues check ✅ Passed The pull request fulfills all coding requirements from issue #597: adds details field to Recommendation and LocalRecommendation interfaces, preserves details in purchase POST via spread syntax, and includes 4 regression tests for EC2 Windows and RDS Postgres fixtures.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #597: type definitions, purchase execution logic, and targeted regression tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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/issue-597-frontend-details

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

@cristim cristim added priority/p0 Drop everything; same-day fix severity/critical Major harm when it happens urgency/now Drop other things impact/all-users Affects every user effort/s Hours type/bug Defect triaged Item has been triaged labels May 20, 2026
@cristim

cristim commented May 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 20, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/all-users Affects every user priority/p0 Drop everything; same-day fix severity/critical Major harm when it happens triaged Item has been triaged type/bug Defect urgency/now Drop other things

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant