The GitHub Actions job "Required Checks" on texera.git/main has failed.
Run started by GitHub user github-merge-queue[bot] (triggered by 
github-merge-queue[bot]).

Head commit for run:
310ab88e4c78da14182284199bde34a1d22d489b / Eugene Gu <[email protected]>
chore(frontend): downloading workflows as a ZIP no longer saves each one 
individually (#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)

Report URL: https://github.com/apache/texera/actions/runs/31668191644

With regards,
GitHub Actions via GitBox

Reply via email to