Skip to content

Commit 803b9a8

Browse files
committed
fix(tables): reject incomplete folder cohorts
1 parent c913829 commit 803b9a8

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
@@ -617,6 +617,30 @@ describe('knowledge_base and table folder resources', () => {
617617
).rejects.toMatchObject({ code: 'locked', message: 'Table deletion is locked' })
618618
})
619619

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

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ async function archiveTableChildren(context: CascadeChildrenContext): Promise<nu
329329
result.failed[0].reason
330330
)
331331
}
332+
if (result.notFound.length > 0 || result.archived.length !== ids.length) {
333+
throw new OrchestrationError(
334+
'conflict',
335+
'One or more tables changed while their folder was being deleted'
336+
)
337+
}
332338

333339
return result.archived.length
334340
}

‎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
@@ -1468,7 +1468,7 @@ export async function deleteTables(
14681468

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

0 commit comments

Comments
 (0)