Skip to content

Commit 2a73242

Browse files
committed
fix(tables): reject incomplete folder cohorts
1 parent 702a0a3 commit 2a73242

4 files changed

Lines changed: 49 additions & 1 deletion

File tree

‎apps/sim/lib/folders/cascade.test.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,30 @@ describe('knowledge_base and table folder resources', () => {
615615
).rejects.toMatchObject({ code: 'locked', message: 'Table deletion is locked' })
616616
})
617617

618+
it('rejects the cohort when a table changes after child selection', async () => {
619+
resetDbChainMock()
620+
queueTableRows(schemaMock.userTableDefinitions, [
621+
{ id: 'tbl_accounts' },
622+
{ id: 'tbl_contacts' },
623+
])
624+
tableServiceMocks.deleteTables.mockResolvedValueOnce({
625+
archived: [],
626+
failed: [],
627+
notFound: ['tbl_contacts'],
628+
})
629+
630+
await expect(
631+
tableConfig.archiveChildren?.({
632+
workspaceId: 'ws-1',
633+
folderIds: ['folder-1'],
634+
timestamp: TIMESTAMP,
635+
})
636+
).rejects.toMatchObject({
637+
code: 'conflict',
638+
message: 'One or more tables changed while their folder was being deleted',
639+
})
640+
})
641+
618642
it('re-archives a successful restore prefix when a later cohort table fails', async () => {
619643
resetDbChainMock()
620644
queueTableRows(schemaMock.userTableDefinitions, [

‎apps/sim/lib/folders/config.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ async function archiveTableChildren(context: CascadeChildrenContext): Promise<nu
361361
result.failed[0].reason
362362
)
363363
}
364+
if (result.notFound.length > 0 || result.archived.length !== ids.length) {
365+
throw new OrchestrationError(
366+
'conflict',
367+
'One or more tables changed while their folder was being deleted'
368+
)
369+
}
364370

365371
return result.archived.length
366372
}

‎apps/sim/lib/table/service.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,24 @@ describe('deleteTables reference guard', () => {
831831
expect(dbChainMockFns.update).not.toHaveBeenCalled()
832832
})
833833

834+
it('archives none of a restore cohort when one selected table is no longer active', async () => {
835+
queueTableRows(schemaMock.userTableDefinitions, [batchTable('tbl_accounts', 'Accounts')])
836+
837+
await expect(
838+
deleteTables(['tbl_accounts', 'tbl_contacts'], 'folder-cascade-folder-1', {
839+
expectedWorkspaceId: WORKSPACE_ID,
840+
skipNotify: true,
841+
archiveAsCohort: true,
842+
})
843+
).resolves.toEqual({
844+
archived: [],
845+
failed: [],
846+
notFound: ['tbl_contacts'],
847+
})
848+
849+
expect(dbChainMockFns.update).not.toHaveBeenCalled()
850+
})
851+
834852
it('allows a self-referencing table to archive', async () => {
835853
queueTableRows(schemaMock.userTableDefinitions, [
836854
batchTable('tbl_categories', 'Categories', ['tbl_categories']),

‎apps/sim/lib/table/service.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,7 @@ export async function deleteTables(
14691469

14701470
const archived: DeleteTablesResult['archived'] = []
14711471
if (options.archiveAsCohort) {
1472-
if (failed.length > 0) return { archived, failed, notFound }
1472+
if (failed.length > 0 || notFound.length > 0) return { archived, failed, notFound }
14731473
const cohort = [...archivePlan.ordered, ...archivePlan.blockedByCycle]
14741474
if (cohort.length > 0) {
14751475
archived.push(

0 commit comments

Comments
 (0)