mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-13 08:02:47 +00:00
fix: do not let a clean editor acknowledge a row it never knew of
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6e0a9a96b3
commit
a5a4f25751
@@ -522,15 +522,19 @@ class Entry<V> {
|
||||
* item, so the row is the caller's. `failed`: has it, on a baseline it cannot refresh. */
|
||||
async discarded(): Promise<'done' | 'absent' | 'failed'> {
|
||||
const asked = this.asOf()
|
||||
// A row this entry has none of is not one it can discard: it does nothing, and the caller
|
||||
// would read that as the row being gone while another tab's is still there.
|
||||
let knewRow = this.row !== null
|
||||
await this.discard(asked)
|
||||
if (this.retired || !this.loaded) return 'absent'
|
||||
if (!this.stale) return 'done'
|
||||
if (!this.stale) return knewRow ? 'done' : 'absent'
|
||||
// Stale refused it. A reload is what clears that, and with a baseline it can trust the
|
||||
// discard means something again — so the one thing worth trying before giving up. The
|
||||
// retry carries the state as of the request, so an edit typed during that reload stands.
|
||||
if (!this.adapter || !(await this.load(this.adapter)).ok) return 'failed'
|
||||
knewRow = this.row !== null
|
||||
await this.discard(asked)
|
||||
return this.stale ? 'failed' : 'done'
|
||||
return this.stale ? 'failed' : knewRow ? 'done' : 'absent'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1127,6 +1127,27 @@ describe('item store: origins', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('says a row it never knew of is not its to discard', async () => {
|
||||
const rows = fakeRows()
|
||||
const store = createItemStore(rows.port)
|
||||
// Loaded clean: no draft existed when this editor read the item.
|
||||
const { handle: item } = store.acquire(
|
||||
{ workspace: 'w', kind: 'resource', path: 'u/me/r' },
|
||||
{ workspace: 'w', path: 'u/me/r' },
|
||||
adapter({ deployed: deployedRes })
|
||||
)
|
||||
await settle()
|
||||
expect(item.dirty).toBe(false)
|
||||
|
||||
// Another tab creates the draft afterwards. This editor knows nothing of it.
|
||||
rows.sent.set('u/me/r', { ...deployedRes, description: 'the other tab' })
|
||||
rows.handExternally('u/me/r')
|
||||
|
||||
// Its discard does nothing, so answering `done` would have the caller skip the delete and
|
||||
// report a draft gone that is still on the server.
|
||||
expect(await store.bridge.discard('w', 'resource', 'u/me/r')).toBe('absent')
|
||||
})
|
||||
|
||||
it('says it did not deal with the row when its own first read never arrived', async () => {
|
||||
const rows = fakeRows()
|
||||
const store = createItemStore(rows.port)
|
||||
|
||||
Reference in New Issue
Block a user