Add bulk archive for selected assets
Angular · Angular · advanced · greenfield
Adds the bulk-archive service behind the assets toolbar. Each selected id maps to an archive POST whose outcome folds into a per-asset result — failures are caught per request so one bad asset can't sink the batch — and the combined stream emits the full results array when everything has settled. Archived a 30-asset selection locally and got 30 ok results back.
Internal digital-asset-management tool. Admins routinely select thousands of assets with the select-all checkbox.
Requirements
- Add `BulkArchiveService.archiveAssets(ids)` for the asset library's bulk actions bar: archive every selected asset via `POST /api/assets/{id}/archive` and emit one `ArchiveResult` (`{ id, ok }`) per asset once all have settled. Result order does not matter — consumers key by `id`.
- Selections can be huge: the library holds tens of thousands of assets and the select-all checkbox selects the entire filtered library. `ids` is always non-empty (the Archive button is disabled with nothing selected) and contains no duplicates.
- The platform team's hard rule for bulk operations: at most 5 archive requests in flight at any moment. The archive endpoint fans out to storage and search-index updates, and unbounded bursts from the admin UI have degraded it before.
- One failed asset must not abort the rest — a failure becomes `{ id, ok: false }` and every other asset still gets archived.
- Use `HttpClient` and RxJS operators (this codebase's standard for composing HTTP).
Files touched
- src/app/assets/bulk-archive.service.ts
--- src/app/assets/bulk-archive.service.ts
+import { Injectable, inject } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, forkJoin, of } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+
+export interface ArchiveResult {
+ id: string;
+ ok: boolean;
+}
+
+@Injectable({ providedIn: 'root' })
+export class BulkArchiveService {
+ private readonly http = inject(HttpClient);
+
+ archiveAssets(ids: string[]): Observable<ArchiveResult[]> {
+ const requests = ids.map((id) =>
+ this.http.post<void>(`/api/assets/${id}/archive`, {}).pipe(
+ map(() => ({ id, ok: true })),
+ catchError(() => of({ id, ok: false })),
+ ),
+ );
+ return forkJoin(requests);
+ }
+}
+