eugenegujing opened a new pull request, #7611: URL: https://github.com/apache/texera/pull/7611
### What changes were proposed in this PR? Selecting several workflows and using the toolbar's "Download added workflow as a ZIP file" action saved one loose `.json` file per selected workflow **in addition to** the archive, so N selected workflows produced N+1 downloads. Root cause is in `frontend/src/app/dashboard/service/user/download/download.service.ts`. `downloadWorkflow(id, name)` both retrieves a workflow and saves it to disk — the save is the `tap(this.saveFile.bind(this))` at the end of the pipe — which is exactly what the per-row download action needs. `createWorkflowsZip` reused that same method purely to obtain each blob, so the save fired for every entry before the blob was added to the archive. This is a regression rather than intended behaviour: before #2920 (`57984370c`, "Refactor Frontend to Centralize Downloads Using DownloadService") the bulk path assembled the zip inline and called `saveAs` exactly once. That refactor moved the logic into `DownloadService` and reused `downloadWorkflow` for retrieval, inheriting its save side effect. The fix splits retrieval from saving: - new private `retrieveWorkflowItem(id, name)` returns the `DownloadableItem` (blob + file name) **without** saving — it is the former body of `downloadWorkflow` minus the `tap`; - `downloadWorkflow` is now `retrieveWorkflowItem(...).pipe(tap(this.saveFile.bind(this)))`, i.e. behaviour is unchanged for the three per-row callers (`user-workflow-list-item.component.ts`, `list-item.component.ts`, `card-item.component.ts`), which subscribe without a value handler and rely solely on that side effect; - `createWorkflowsZip` calls `retrieveWorkflowItem` directly. `A.pipe(map, tap)` and `A.pipe(map).pipe(tap)` compose the same chain, so the emitted value, timing, subscription semantics and error propagation of `downloadWorkflow` are unchanged. The public API is untouched and no call site needed updating. **Before** — three workflows selected, then "Download as ZIP": the archive plus `test1.json`, `test2.json`, `test3.json`, four files in total. <img width="1493" height="755" alt="Screenshot 2026-08-12 at 3 48 45 PM" src="https://github.com/user-attachments/assets/7a7b8cb0-330a-452f-b853-b7aabfc04acd" /> **After** — same three workflows, same action: only `workflowExports-*.zip`. <img width="1482" height="750" alt="Screenshot 2026-08-12 at 4 43 38 PM" src="https://github.com/user-attachments/assets/bdc64245-91bc-4fa0-a06d-2b80e2871706" /> ### Any related issues, documentation, discussions? Closes #7608 ### How was this PR tested? Seven cases were added to `frontend/src/app/dashboard/service/user/download/download.service.spec.ts` (23 → 30). Three of them fail on `main` and pass with this change. Pinning the fix: - `saves only the zip, not one JSON per workflow, when several workflows are zipped` — three workflows, asserts `saveAs` is called exactly once with the archive, and that no `Alpha.json` / `Beta.json` / `Gamma.json` was saved - `saves only the zip for a single-workflow selection` — the N=1 boundary, which a plain "one extra file" check would miss Guarding the other direction, so the bug cannot be "fixed" by deleting the save: - `still saves the file when a single workflow is downloaded on its own` — passes before and after; it fails if `downloadWorkflow` stops saving, which would break the per-row download action Edge cases: - `saves nothing when one of the workflows fails to retrieve` — the archive aborts as a whole, so the workflows that did come back must not be left behind as loose files (previously they were already saved by the time `forkJoin` errored) - `writes the workflow content into the zip entries` — reads an entry back out of the produced archive and parses it, since the pre-existing tests only asserted entry *names* - `does not save anything when the standalone workflow download fails` — error propagates, nothing is written; mirrors the existing dataset / single-file error cases - `saves nothing for an empty selection` — `forkJoin([])` completes without emitting, so nothing is retrieved and no empty archive is written (the toolbar already guards this case) ``` cd frontend node --max-old-space-size=8192 ./node_modules/@angular/cli/bin/ng test --watch=false \ --include="src/app/dashboard/service/user/download/download.service.spec.ts" ``` `Test Files 1 passed (1)` / `Tests 30 passed (30)`. Reverting only the source change makes exactly three of them fail. The specs of the three components that depend on `downloadWorkflow` still saving were also run and pass unchanged (`list-item`, `card-item`, `user-workflow-list-item`). Manually verified against a local stack, as shown in the screenshots above: create three workflows, select them, click the ZIP download action, and compare the browser's download list before and after the change. ### Was this PR authored or co-authored using generative AI tooling? Co-authored by: Claude Code (Claude Opus 5) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
