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:
ee06e4ba05f937f43041f7dc7faaca11d6759f9b / Ryan Zhang 
<[email protected]>
feat(notebook-migration): remove a workflow's notebook file from the Jupyter 
pod on delete (#7741)

### What changes were proposed in this PR?
Removes a workflow's notebook file from the Jupyter pod when the
notebook is deleted, so the pod's `work/` directory no longer
accumulates notebooks for workflows the user has removed.

Before #7671 this was self limiting: every workflow uploaded to the same
`work/notebook.ipynb`, so there was only ever one file and it was
overwritten on reuse. Now that each workflow uploads to
`work/notebook_<wid>.ipynb`, the file survives both a notebook delete
and a workflow delete, because those paths only touched the database.

The database is authoritative for whether a notebook exists. The pod
file is a per user artifact only the frontend can reach, since the
notebook migration service targets one Jupyter per process under the per
user pod model. So the file cleanup is best effort from the frontend
after the authoritative database delete, and a failure is logged rather
than surfaced.

**`NotebookMigrationResource` (new `delete-notebook` endpoint)**
- Adds `POST /notebook-migration/delete-notebook`, the counterpart to
`set-notebook`: it takes a `notebookName`, validates it with the same
`[A-Za-z0-9._-]+\.ipynb` pattern (blocking path traversal before any
network call), and issues `DELETE /api/contents/work/<name>` against the
Jupyter Contents API, bounded by a 2s connect and read timeout so a
stalled pod cannot wedge the request thread.
- A 204 or 200 reports `deleted: 1`. A 404 is treated as a no op with
`deleted: 0`, so a workflow whose notebook was never uploaded still
deletes cleanly, consistent with how the database delete reports
`deleted: 0` when nothing was stored. Any other status is a 500.
- Extracts the shared `jupyterUnavailableResponse` so the four endpoints
that need a reachable Jupyter cannot drift in status or body.

**`NotebookMigrationService` (frontend)**
- Adds `deleteNotebookForWorkflow(wid)`, the single seam both delete
paths call. It derives the filename from the wid, so the
`notebook_<wid>.ipynb` convention lives only in the service that owns
it, posts it to the new endpoint, and returns nothing: pod cleanup is
best effort, so a failure is logged, not surfaced, and no caller acts on
the outcome. The parameter is a concrete wid so it can never fall back
to the shared default filename.

**`JupyterPanelService` (panel delete button)**
- `deleteJupyterNotebook()` captures the current wid up front (so a mid
flight workflow switch cannot retarget the delete), then calls
`deleteNotebookForWorkflow(wid)` after the database delete succeeds. The
unsaved workflow path (wid undefined or the default 0) still resets
local state only, since no file was ever uploaded for it.

**`UserWorkflowComponent` (dashboard delete)**
- Adds a private `cleanupNotebookFiles(wids)` that calls
`deleteNotebookForWorkflow(wid)` per wid, and calls it from the success
handler of both single delete (`deleteWorkflow`) and bulk delete
(`handleConfirmDeleteSelectedWorkflows`). It runs only after the backend
delete succeeds, so a failed delete leaves the pod file in place. A
deleted workflow with no notebook produces a harmless 404.

### Any related issues, documentation, discussions?
Closes #7737
Parent issue #4301

### How was this PR tested?
- `NotebookMigrationResourceSpec.scala`: `delete-notebook` issues a
DELETE against the `work/<name>` contents path (verb and path pinned),
reports `deleted=1` on 204 and on 200, treats 404 as `deleted=0`,
returns 500 when Jupyter rejects the delete or is unreachable, and
returns 400 on an invalid name, a missing or non string name, or a
malformed body.
- `notebook-migration.service.spec.ts`: `deleteNotebookForWorkflow`
posts the wid-derived name to `delete-notebook`, swallows a transport
failure without notifying, and makes no HTTP call when the feature flag
is off.
- `jupyter-panel.service.spec.ts`: the panel delete calls
`deleteNotebookForWorkflow` with the current wid, does not touch the pod
when the database delete fails or for the unsaved default wid, and no
ops when the flag is off.
- `user-workflow.component.spec.ts`: single delete cleans up wid 5, bulk
delete cleans up each checked wid in order, and neither the no wid path
nor a backend delete error touches the pod.

### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)

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

With regards,
GitHub Actions via GitBox

Reply via email to