The GitHub Actions job "Required Checks" on 
texera.git/gh-readonly-queue/main/pr-8540-ba2667a2259b6f5d7442979e5111d885ab57a4a5
 has succeeded.
Run started by GitHub user mengw15 (triggered by mengw15).

Head commit for run:
e0f49897dbb525dc7faa6eac5f293fdb02b8f7f9 / yangzhang75 <[email protected]>
fix(workflow): keep a newer local edit over a save's response (#8540)

### What changes were proposed in this PR?

Closes #8536, the item deferred from #8456 on review.

Every save's response is fed back as the workflow's metadata (the canvas
autosave in `workspace.component.ts`, the menu's own save for a rename /
description / revert, the Form View switch's save). Saves go out one at
a time, so a response can land after a newer local edit and put the old
name or description back: rename while an autosave is out and the title
flips back until the rename's own save answers; rename while the Form
View switch's save is out and the rename is lost: the switch's own
response puts the old name back, the hand-over leaves at once, and the
canvas's save on its way out (`persistBeforeLeaving`) stores that old
name, queued behind the rename's own save. (When this PR was opened the
switch was a page load that aborted the rename's save outright; since
#8581 made it a route the save is sent, and the rename is lost this way
instead.)

Handled once, in `WorkflowPersistService`, the one place every save goes
through:

- Each response is relayed with the page's current name and description
in place of the ones the save was sent with. Those are the two fields a
user edits; everything else in the response (id, timestamps, publish
state, default view) is the server's and arrives as before. A response
is left alone when another workflow is open by the time it answers, or
when the page was cleared meanwhile; the local name is kept for a
workflow the save has just created (the page still holds the default
id), which the id the save was sent with tells apart from a cleared
page, since that one also holds the default id. The action service is
looked up lazily at response time, so the dashboard, which also uses
this service (retrieve, create, duplicate), does not construct the
graph-owning service as a side effect. The callers that feed a response
back as metadata (the workspace autosave, the menu's own save, the Form
View switch, the form's save) are unchanged by this part; the settings
panel's save and the workspace's unload save never read the response.
- `whenSavesDrained()` emits once every save asked for so far has
answered or failed (at once when none is pending). The Form View switch
waits for it before leaving, so a save queued behind its own (a
rename's, a description's, which save through the menu itself and do not
go through `workflowChanged`) has answered while the menu that asked for
it is still there to show its error or feed back its response, the same
reason the switch already waits for its own save. A queued save that
fails reports its error through its own caller and does not hold the
hand-over. The wait is bounded (`HANDOVER_DRAIN_TIMEOUT_MS`, 10 s): a
queued save that never answers does not hold the switch either; past the
bound it leaves as it did before the wait existed.

Verified in a real browser against today's main and this branch side by
side (two dev servers on one backend, every persist request held 1.5 s
by a proxy in front of each). Before: a rename made while an earlier
rename's save was out flipped the title back to the old name when that
response landed (1.6 s) until the second answered (3.2 s); a rename made
during the Form View switch's save ended with the old name shown in the
Form View and stored (the switch left at 1.5 s, the canvas's save on its
way out carried the reverted name). After: the title stays on the new
name; the switch leaves once the rename's save has landed (3.1 s) and
the Form View shows, and the server stores, the new name; an operator
dragged while the hand-over waited for the queue was saved before the
route, with its new position in the stored content.

### Any related issues, documentation, discussions?

Closes #8536. Follow-up to #8456 (threads on `menu.component.ts:692` and
`:231`); part of the Form View feature (parent issue #8011).

### How was this PR tested?

Unit tests (vitest): the persist service relays a response with the
page's current name and description and the server's other fields, keeps
the local name for a just-created workflow, leaves a response alone when
another workflow is open or when the page was cleared while the save was
out; `whenSavesDrained` emits at once when idle, only after the last of
two queued saves has answered, and after a failed save; the menu's
switch leaves only once the queue has drained, and an edit made while it
waits for the queue is saved before it leaves; the service answers a
caller asking from its own save's complete callback only once the save
queued behind has answered too, and answers a call once (a drain caused
by a later save does not reach a caller answered already); the menu's
switch leaves after the bound when a queued save never answers, without
saving again behind it. Each new guard was deletion-checked (removing it
turns the corresponding test red). eslint, prettier and the production
(AOT) build pass; every changed line is statement and function covered.

### Was this PR authored or co-authored using generative AI tooling?

Yes. Generated-by: Claude Code (Claude Fable 5.1, Anthropic).
Co-authored with Claude, reviewed line by line by the author before
submission.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01FVvP3ttj22f9LB4p9u2anY

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>

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

With regards,
GitHub Actions via GitBox

Reply via email to