mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-18 08:03:11 +00:00
fix: let a deployed item's editor settle its own row instead of discarding it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b4ed4f52c5
commit
184af46829
@@ -2347,9 +2347,10 @@ describe('global AI tools', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// Deploying a draft writes the item, so an editor open on it is a baseline behind. Its row is
|
||||
// discarded straight after, which resets it to that baseline — so it has to re-read first.
|
||||
it('refreshes a live editor before a deploy discards its draft', async () => {
|
||||
// Deploying a draft writes the item, so an editor open on it is a baseline behind. Re-reading
|
||||
// is the whole cleanup: discarding on top would throw away anything typed during the deploy,
|
||||
// which was never part of it.
|
||||
it('re-reads a live editor after a deploy and leaves the cleanup to it', async () => {
|
||||
const path = 'u/admin/deployed_res'
|
||||
seedBackendDraft('resource', path, { path, value: { a: 2 } })
|
||||
const calls: string[] = []
|
||||
@@ -2371,7 +2372,9 @@ describe('global AI tools', () => {
|
||||
preserveLiveDraft: true,
|
||||
deployed: true
|
||||
})
|
||||
expect(calls).toEqual(['refresh', 'discard'])
|
||||
expect(calls).toEqual(['refresh'])
|
||||
// The chat sends no delete of its own either: the row is the editor's to settle.
|
||||
expect(getBackendDraft('resource', path)).toBeDefined()
|
||||
} finally {
|
||||
registerLiveItemBridge({
|
||||
seed: () => false,
|
||||
|
||||
@@ -595,7 +595,20 @@ export async function deleteGlobalDraft(
|
||||
const itemKind = itemKindFor(type, triggerKind)
|
||||
if (!itemKind) return
|
||||
const storagePath = resolveDraftStoragePath(workspace, itemKind, path)
|
||||
if (options.deployed) await refreshLiveItem(workspace, itemKind, storagePath)
|
||||
if (options.deployed) {
|
||||
const refreshing = refreshLiveItem(workspace, itemKind, storagePath)
|
||||
if (refreshing) {
|
||||
// Re-reading the open editor is the whole cleanup, and discarding on top of it is
|
||||
// wrong: the deploy carried the draft as it stood when it started, so anything typed
|
||||
// since is a divergence from what was deployed and belongs in the row. The editor's
|
||||
// own rule already drops the row when the two match and keeps it when they do not.
|
||||
await refreshing
|
||||
// Its reconcile only queued that; send it, so a caller awaiting this sees the server
|
||||
// settled — with no draft, or with the newer edit.
|
||||
await UserDraftDbSyncer.flush({ workspace, itemKind, path: storagePath })
|
||||
return
|
||||
}
|
||||
}
|
||||
const liveDraft = UserDraft.getLiveEditorDraft(itemKind, { workspace })
|
||||
const live =
|
||||
options.preserveLiveDraft && liveDraft?.storagePath === storagePath
|
||||
|
||||
@@ -721,6 +721,37 @@ describe('item store: origins', () => {
|
||||
expect(rows.writes.at(-1)).toEqual({ path: 'u/me/r', value: fromChat })
|
||||
})
|
||||
|
||||
it('settles its own row when its draft is deployed, keeping anything typed during it', async () => {
|
||||
const rows = fakeRows()
|
||||
const store = createItemStore(rows.port)
|
||||
const key: ItemKey = { workspace: 'w', kind: 'resource', path: 'u/me/r' }
|
||||
const drafted = { ...deployedRes, description: 'the draft being deployed' }
|
||||
let deployed = deployedRes
|
||||
const { handle: item } = store.acquire(
|
||||
key,
|
||||
{ workspace: 'w', path: 'u/me/r' },
|
||||
adapter(async () => ({ deployed, draft: (rows.sent.get('u/me/r') as Res) ?? drafted }))
|
||||
)
|
||||
await settle()
|
||||
expect(item.value).toEqual(drafted)
|
||||
|
||||
// The deploy writes the draft to the item, then asks the editor to re-read.
|
||||
deployed = drafted
|
||||
expect(await item.reload()).toEqual({ ok: true })
|
||||
// Nothing was typed during it, so the row it was holding is now redundant and goes on its
|
||||
// own. This is the cleanup, and is why the deploy must not discard on top of it.
|
||||
expect(item.dirty).toBe(false)
|
||||
expect(rows.writes.at(-1)).toEqual({ path: 'u/me/r', value: null })
|
||||
|
||||
// Now the same again with an edit made while the deploy was in flight.
|
||||
const typed = { ...deployedRes, description: 'typed while deploying' }
|
||||
item.value = typed
|
||||
await rows.port.flush(key)
|
||||
deployed = typed
|
||||
expect(await item.reload()).toEqual({ ok: true })
|
||||
expect(item.value).toEqual(typed)
|
||||
})
|
||||
|
||||
it('keeps a draft-only item an outside write put back while its delete was going', async () => {
|
||||
const rows = fakeRows()
|
||||
const draft = { ...deployedRes, description: 'only a draft' }
|
||||
|
||||
Reference in New Issue
Block a user