Skip to content

Bulk delete reports real settled outcomes with live progress - #5680

Merged
norman-abramovitz merged 11 commits into
cloudfoundry:developfrom
nabramovitz:feat/bulk-delete-progress
Jul 22, 2026
Merged

Bulk delete reports real settled outcomes with live progress#5680
norman-abramovitz merged 11 commits into
cloudfoundry:developfrom
nabramovitz:feat/bulk-delete-progress

Conversation

@nabramovitz

Copy link
Copy Markdown
Contributor

Bulk delete (apps and routes) previously ended with "N applications delete requested" because async CF deletes come back PENDING inside the bulk envelope and the frontend never tracked them to completion. This wires up the tracking the backend already supports: each PENDING item in a BulkResult carries a stratosjobs handoff, and the frontend now polls those jobs to settlement and reports what actually happened.

What the user sees now: from the moment the delete is confirmed, a snackbar shows "Deleting N applications…", updating to "… X of N" as async items settle, ending in "N applications deleted" (auto-dismisses after ~5s) or, when something went wrong, "X deleted, Y failed" / "… Y unconfirmed" — which stays until dismissed and triggers a list refresh so wrongly-removed rows resurface. The word "requested" is gone. Route bulk delete gets the same treatment; route unmap (synchronous server-side) gets the aligned final-summary wording with no polling phase.

How

  • pollBulkResult() (new, in services/async-jobs) settles a BulkResult: fast-path COMPLETE/FAILED items count immediately, PENDING items are polled via the existing pollJob (exported from write-with-job) with bounded concurrency (6, matching the backend fan-out width). Poll outcomes: COMPLETE → deleted, job FAILED → failed, UNKNOWN (404/HA rule) → unconfirmed. Never rejects.
  • runBulkWithProgress() (same module) owns the lifecycle: opens the progress snackbar at operation start, runs the POST phase (bulk-bar spinner, optimistic row removal unchanged), resolves for the caller at POST end, and continues settlement detached — the config services are root-scoped and the snackbar is global, so progress survives navigation. All snackbar/refresh errors are contained; nothing can escape as an unhandled rejection.
  • The snackbar service ref gains update(message) (in-place message updates), made explicitly inert after dismiss() to close the race with late settlement updates.
  • All four list components (CF apps, space apps, CF routes, space routes) delegate their hand-rolled runBulk to the shared helper.

Latent bug fixed along the way: the frontend typed BulkItemResult.state as lowercase ('succeeded' | 'failed' | 'pending') but the backend sends uppercase (COMPLETE/FAILED/PENDING), so the item.state !== 'failed' guard never matched and failed deletes were optimistically removed from the list along with successful ones. Types now match the wire (including job being a full job object, not a string) and a guard test pins the behavior.

Verification: full gate green (lint, all 8 vitest projects — 640 files, production build, backend tests). Live-verified against a real CF: progress snackbar visible through the POST and polling phases, per-item job polls on the wire, final summary correct, failed-path wording covered by unit tests. Version bumped to v5.0.0-dev.139.

Previously runBulkWithProgress only opened the progress snackbar
after the POST returned, and only when settlement had PENDING
items — a fast bulk delete gave no visible feedback until the
final summary. BulkProgressOptions now accepts an optional
`count` (the caller's known selection size); when provided and
> 0, the progress snackbar opens before op() is awaited, with a
plain "Verb N nouns…" message. The same ref threads into
settlement, which upgrades it in place to the "…N of M" wording
(M from the authoritative BulkResult) and dismisses it before the
final summary; POST failure dismisses it before the error
snackbar. The no-count / no-PENDING fallback path (lazy open
during settlement) is unchanged. All four bulk-delete callers
(CF Applications, Space Apps, CF Routes, Space Routes) now pass
their selection count through runBulk().

@norman-abramovitz norman-abramovitz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM - spinner is still not working but the messaging makes you want to wait.

@norman-abramovitz
norman-abramovitz merged commit cfb011b into cloudfoundry:develop Jul 22, 2026
22 checks passed
@nabramovitz
nabramovitz deleted the feat/bulk-delete-progress branch July 22, 2026 15:50
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.

2 participants