Svdvd-349 Jun 2026

| # | Given | When | Then | |---|-------|------|------| | | A document with ≥ 2 attachments is opened in the viewer. | The UI renders. | The toolbar shows a primary‑styled button labelled “Download All Attachments”. | | AC‑2 | The button is clicked. | The client calls the zip‑endpoint. | A download prompt appears with filename Document_<docId>_attachments_<timestamp>.zip . | | AC‑3 | The zip contains every attachment (including hidden ones the user can view). | The download completes. | The ZIP’s internal structure mirrors the original ordering (e.g., attachments/001‑Invoice.pdf ). | | AC‑4 | The total size of all attachments exceeds 500 MB . | The user clicks the button. | The API returns HTTP 413 with JSON error: "PayloadTooLarge", message: "Attachments exceed 500 MB limit." and the UI shows a toast: “Too many files – please download individually or request a larger bundle via Support.” | | AC‑5 | The request is made by a user lacking download permission for one of the attachments. | The API processes the request. | The response is HTTP 403, and the UI disables the button (grayed out) with tooltip “You do not have permission to download all files.” | | AC‑6 | The request is made on a mobile Safari browser. | The response returns a streaming ZIP. | The browser shows the native “Share / Save to Files” dialog (no “download‑blocked” warnings). | | AC‑7 | The zip is generated successfully. | The user opens it locally. | A manifest.txt file exists at the root, containing lines: <checksum> <size> <relative‑path> . | | AC‑8 | Automated test suite runs. | All unit, integration, and end‑to‑end tests pass. | Coverage for the new endpoint ≥ 90 %, UI interaction tests pass on Chrome, Firefox, Safari, Edge. |

| Layer | Details | |------|----------| | | GET /api/v1/documents/docId/attachments/zip | | Auth | Inherit existing JWT + RBAC. Verify DOWNLOAD_ATTACHMENT permission for each attachment; if any fail → 403. | | Streaming | Use Spring Boot (or equivalent) ResponseBodyEmitter / StreamingResponseBody to stream ZIP on‑the‑fly (no temporary files). | | Zip Creation | - Use java.util.zip.ZipOutputStream (or Apache Commons Compress). - Add manifest.txt as the first entry. - Preserve original filenames; if duplicate names exist, prefix with numeric index. | | Size Guard | Before streaming, compute total size via metadata query. If > 500 MB → 413. | | Error Handling | Convert checked exceptions to JSON error responses via @ControllerAdvice . | | Rate Limiting | Apply existing per‑user API rate limiter (e.g., 10 zip requests/min). | | Metrics | Increment attachments.zip.request counter, record duration, success/failure tags. | SVDVD-349

| # | Task | Owner | Estimate | |---|------|-------|----------| | | Add UI button, tooltip, loading state | Frontend (React) | 2 d | | T‑2 | Implement useDownloadAll hook + error handling | Frontend | 1 d | | T‑3 | Write unit & e2e tests for frontend | QA/Frontend | 1 d | | T‑4 | Create backend endpoint, permission checks | Backend (Java) | 2 d | | T‑5 | Implement streaming ZIP with manifest generation | Backend | 2 d | | T‑6 | Add size‑limit guard & proper error codes | Backend | 0.5 d | | T‑7 | Update API documentation (OpenAPI) | Docs | 0.5 d | | T‑8 | Add audit‑log entry | Backend | 0.5 d | | T‑9 | Performance testing (large payload) | Performance Engineer | 1 d | | T‑10 | Review, security scan, merge | All | 1 d | | Total | | | ~11 person‑days | | # | Given | When | Then

I [recommend/do not recommend] [Product Name] for [specific audience or use cases]. | | AC‑2 | The button is clicked