From 449b27e10b0184984939d7deaeb83e1e7316f2a7 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Date: Wed, 18 Mar 2026 14:32:22 +0530 Subject: [PATCH 1/2] fix: upsert vCon on duplicate UUID submission Replace .insert() with .upsert({ onConflict: 'id' }) for the main vcon row, and delete all child rows (parties, dialog, analysis, attachments) before re-inserting. This makes re-submitting the same vCon UUID idempotent instead of returning a 500 duplicate key error. Co-Authored-By: Claude Sonnet 4.6 --- src/db/queries.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/db/queries.ts b/src/db/queries.ts index e64022d..c3d6c61 100644 --- a/src/db/queries.ts +++ b/src/db/queries.ts @@ -69,11 +69,11 @@ export class VConQueries { } } - // Insert main vcon + // Upsert main vcon — idempotent on re-submission of same UUID // Set id = uuid so they match (id is the PK, uuid is the vCon document UUID) const { data: vconData, error: vconError } = await this.supabase .from('vcons') - .insert({ + .upsert({ id: vcon.uuid, // Explicitly set id to match uuid uuid: vcon.uuid, vcon_version: vcon.vcon ?? '0.3.0', @@ -85,7 +85,7 @@ export class VConQueries { redacted: vcon.redacted || {}, appended: vcon.appended || {}, // ✅ Added per spec tenant_id: tenantId, // ✅ Added for RLS multi-tenant support - }) + }, { onConflict: 'id' }) .select('id, uuid') .single(); @@ -97,6 +97,14 @@ export class VConQueries { throw vconError; } + // Delete existing child rows so re-submission replaces them cleanly + await Promise.all([ + this.supabase.from('parties').delete().eq('vcon_id', vconData.id), + this.supabase.from('dialog').delete().eq('vcon_id', vconData.id), + this.supabase.from('analysis').delete().eq('vcon_id', vconData.id), + this.supabase.from('attachments').delete().eq('vcon_id', vconData.id), + ]); + // Insert parties if (vcon.parties.length > 0) { const partiesData = vcon.parties.map((party, index) => ({ From 542d72234ab2aded68e2031b7ea6fd168e554c92 Mon Sep 17 00:00:00 2001 From: Pavan Kumar Date: Wed, 18 Mar 2026 14:47:50 +0530 Subject: [PATCH 2/2] test: add upsert mock and update createVCon assertion Add `upsert` to the chainable Supabase mock and update the createVCon test assertion from `insert` to `upsert` to match the new upsert behavior introduced in the previous commit. Co-Authored-By: Claude Sonnet 4.6 --- tests/db-queries.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/db-queries.test.ts b/tests/db-queries.test.ts index 1378b93..10bbb11 100644 --- a/tests/db-queries.test.ts +++ b/tests/db-queries.test.ts @@ -19,6 +19,7 @@ describe('VConQueries', () => { from: vi.fn(), select: vi.fn(), insert: vi.fn(), + upsert: vi.fn(), update: vi.fn(), delete: vi.fn(), eq: vi.fn(), @@ -70,7 +71,7 @@ describe('VConQueries', () => { expect(result.uuid).toBe(testVCon.uuid); expect(mockSupabase.from).toHaveBeenCalledWith('vcons'); - expect(mockSupabase.insert).toHaveBeenCalled(); + expect(mockSupabase.upsert).toHaveBeenCalled(); }); it('should create vCon with all components (dialog, analysis, attachments)', async () => {