Skip to content

fix(frontend): offer Revoke on scheduled AWS and GCP executions - #1393

Merged
IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-924
Oct 24, 2026
Merged

IAmJSD merged 1 commit into
LeanerCloud:mainfrom
IAmJSD:ui/issue-924

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

On Purchase History, the Revoke button only appeared on Azure rows, including scheduled ones. That rule fits completed purchases (only Azure reservations can be returned after purchase), but a scheduled execution hasn't reached any cloud yet and the backend cancels it for free on every provider, so AWS and GCP scheduled purchases had no UI path to stop them before they fired. This PR applies the Azure-only rule to completed rows only and makes the scheduled-row confirm copy provider-neutral.

Closes #924

What changed

  • frontend/src/history.ts: canRevokeCompletedRow applies the Azure-only check to completed and legacy blank-status rows only, so scheduled rows of any provider get Revoke. The window check (revocation_window_closes_at in the future), the revoked_at check and the revoke-any/revoke-own RBAC check are unchanged.
  • The confirm dialog for a scheduled row no longer says "This will request an Azure reservation return". It now reads "This will cancel the scheduled purchase. It has not been submitted yet, so nothing will be charged. This action cannot be undone." Completed Azure rows keep the quote-based refund copy. Comments updated to match.
  • Tests: 7 new cases in __tests__/history-revoke-button.test.ts and new tests-e2e/history-scheduled-revoke.spec.ts.

Why this approach

It is the smallest change that matches the backend, which was checked first, as the issue asks:

  • internal/api/handler_purchases_revoke.go:139 revokePurchase sends any ID that resolves to an execution to revokeScheduledExecution (CancelScheduledExecutionAtomic, no provider SDK call), whatever the provider.
  • internal/api/handler_history.go:455-463 sets RevocationWindowClosesAt from ScheduledExecutionAt for every scheduled row, so the existing window gate works for AWS and GCP too.
  • Server-side RBAC for scheduled rows (authorizeSessionRevokeExecution) is also provider-agnostic.

The function name canRevokeCompletedRow is kept to avoid churn, even though it now covers scheduled rows too.

Screenshots

Before is main (a14dc8f2), after is this branch. Both are headless Chromium at 1440x900 against the repo's mocked e2e API, with a mocked /api/history holding scheduled AWS, GCP and Azure rows plus a completed AWS row.

Before After
scheduled-rows before scheduled-rows after
scheduled-confirm before scheduled-confirm after
  • Rows: of the three scheduled rows, only Azure has Revoke on main; AWS and GCP show -. After, all three scheduled rows offer Revoke, and the completed AWS row still has no Revoke (only Sell on Marketplace).
  • Confirm for a scheduled row: main says "This will request an Azure reservation return" even though nothing has been bought. After, provider-neutral copy: "This will cancel the scheduled purchase. It has not been submitted yet, so nothing will be charged."

Verification

Run on this branch's head d08080bc on top of main a14dc8f2:

  • Lint: 0 errors (142 existing warnings). Typecheck and build pass.
  • Jest: 109 suites, 3494 passed, 1 skipped. New tests in history-revoke-button.test.ts (7): scheduled AWS, GCP and Azure each show Revoke; completed AWS and GCP still hidden; scheduled AWS with a past fire time hidden; scheduled GCP without a revoke permission hidden; clicking Revoke on a scheduled AWS row cancels with no quote request and no "Azure" in the copy. The file passes with --randomize (21/21).
  • Playwright via e2e-local.sh: 159 passed, including the new history-scheduled-revoke.spec.ts (2 tests).
  • git-secrets scan clean (planted key flagged); scripts/test-git-secrets-allowlist.sh: 137 pass.

Notes for review

  • New copy in the scheduled confirm dialog. The dialog title ("Revoke this purchase within the free-cancel window?") and button ("Revoke purchase") are unchanged.
  • Completed AWS rows keep showing "Sell on Marketplace" and no Revoke.
  • The backend doesn't pre-reject a scheduled row whose fire time has passed but is still scheduled (scheduler lag). The frontend still hides Revoke for those rows, as before, because the issue asks to keep the revocation-window check.

Overlaps

Those PRs will be opened alongside this one and merged in sequence, with conflicts resolved on the later branch.

Note for the UI owner

This changes UI code that @cristim owns. IAmJSD reviewed the before/after screenshots and asked for it to be merged; any design point you disagree with can be a follow-up.

🤖 Generated with Claude Code

The History Revoke button was gated on provider === 'azure' for every
row. That fits completed rows, since only Azure reservations can be
returned after purchase, but a scheduled row has not called any cloud
yet: the backend cancels it with CancelScheduledExecutionAtomic for
every provider, and stamps revocation_window_closes_at with the fire
time regardless of provider. AWS and GCP scheduled executions therefore
had no UI path to stop them before they fired.

Apply the Azure restriction to completed (and legacy blank-status) rows
only. Scheduled rows keep the existing window and revoke RBAC checks.
The scheduled confirm copy no longer promises an Azure reservation
return, since the row may be AWS or GCP.

Refs LeanerCloud#924

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD IAmJSD added triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/this-quarter Within the quarter impact/few Limited audience effort/xs Trivial / one-liner type/bug Defect labels Oct 24, 2026
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Caution

Review failed

The pull request is closed.

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): APPROVE d08080bcd139902fd46ffc0414bce6300f72ee11

Blocking:

  • none
    Non-blocking:
  • frontend/src/history.ts:1512: the success toast still says "Purchase revocation submitted" for a scheduled-row cancel, which completes synchronously. Copy nit.
  • canRevokeCompletedRow now also gates scheduled rows, so the name is misleading. The author acknowledged this.
    Attacked: Verified the PR's backend claims in source. revokePurchase routes any execution ID to revokeScheduledExecution (CAS on status='scheduled', no provider call, 410 if already fired). annotateInFlightOrAuditGapRow sets RevocationWindowClosesAt from ScheduledExecutionAt for every provider. Checked that the provider gate still applies to completed and legacy-blank rows, that the window/revoked_at/RBAC checks are unchanged, and that scheduled rows skip the Azure quote call and get provider-neutral copy. Checked that there is no overlap with the Cancel or Marketplace buttons on scheduled rows.

@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at d08080bc merged onto current main (84b543d2; merge-tree 113af027; Linux, Node, headless Chromium):

  • This branch's own checks passed at this head: lint, typecheck, build, the full jest suite, the full Playwright e2e suite, and the git-secrets checks (see Verification in the PR body).
  • The combined tree of every UI PR in this merge wave (tree 89dd34d8, main plus 46 PRs) passes npm ci, lint, typecheck, build, jest (1 skipped, 3914 passed, 3915 total), and the full Playwright suite (208 passed).
  • CI is green on d08080bc (CI Success, Run pre-commit hooks).
  • The independent review above approves this exact head.
  • Nothing was applied to any cloud.

@IAmJSD
IAmJSD marked this pull request as ready for review October 24, 2026 17:44
@IAmJSD
IAmJSD merged commit 4950e45 into LeanerCloud:main Oct 24, 2026
29 checks passed
@IAmJSD

IAmJSD commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Marked ready and merged by IAmJSD's agent, at IAmJSD's request (IAmJSD reviewed the before/after screenshots; cristim is traveling), after the independent review and local verification above, all at d08080bc.

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

Labels

effort/xs Trivial / one-liner impact/few Limited audience priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(frontend): offer Revoke on scheduled AWS and GCP executions, not only Azure

1 participant