fix(GRO-2235): return 409 on duplicate portal waitlist submit #189

Merged
Flea Flicker merged 2 commits from flea/gro-2235-waitlist-duplicate-409 into dev 2026-06-08 23:50:22 +00:00
Member

GRO-2235 — Portal Book New: duplicate waitlist submit returns 500 instead of 409

Source: triage of GRO-2233.

Problem

POST /api/portal/waitlist inserted the entry without catching the partial
unique-index violation idx_waitlist_active_unique
(client_id, pet_id, service_id, preferred_date, preferred_time) WHERE status='active'.
An exact duplicate active booking threw an unhandled SQLSTATE 23505, which
surfaced as a generic 500.

Fix

Wrap the insert in a try/catch in the handler. On 23505 return
409 Conflict with {"error":"You already have a booking for this pet at that date and time."}.
Any other error is re-thrown and still surfaces as 500. The first insert is
unchanged and still returns 201.

Acceptance criteria

  • Duplicate submission → 409 with friendly JSON message (not 500)
  • Catches Postgres unique-violation (23505) specifically; unrelated errors still 500
  • First insert still returns 201

Tests

New src/__tests__/portalWaitlistDuplicate.test.ts:

  • first insert → 201
  • identical duplicate (mocked 23505) → 409 friendly message
  • unrelated DB error (23502) → 500

npx tsc --noEmit clean; portal + waitlist suites pass (83/83).

cc @cpfarhood

## GRO-2235 — Portal Book New: duplicate waitlist submit returns 500 instead of 409 **Source:** triage of GRO-2233. ### Problem `POST /api/portal/waitlist` inserted the entry without catching the partial unique-index violation `idx_waitlist_active_unique` `(client_id, pet_id, service_id, preferred_date, preferred_time) WHERE status='active'`. An exact duplicate active booking threw an unhandled SQLSTATE `23505`, which surfaced as a generic `500`. ### Fix Wrap the insert in a try/catch in the handler. On `23505` return `409 Conflict` with `{"error":"You already have a booking for this pet at that date and time."}`. Any other error is re-thrown and still surfaces as `500`. The first insert is unchanged and still returns `201`. ### Acceptance criteria - [x] Duplicate submission → `409` with friendly JSON message (not `500`) - [x] Catches Postgres unique-violation (`23505`) specifically; unrelated errors still `500` - [x] First insert still returns `201` ### Tests New `src/__tests__/portalWaitlistDuplicate.test.ts`: - first insert → `201` - identical duplicate (mocked `23505`) → `409` friendly message - unrelated DB error (`23502`) → `500` `npx tsc --noEmit` clean; portal + waitlist suites pass (83/83). cc @cpfarhood
Flea Flicker added 1 commit 2026-06-08 23:44:29 +00:00
fix(GRO-2235): return 409 on duplicate portal waitlist submit
CI / Test (pull_request) Successful in 26s
CI / Lint & Typecheck (pull_request) Successful in 31s
CI / Build & Push Docker Images (pull_request) Failing after 48s
d06c3b8d31
An exact duplicate active waitlist entry (same client/pet/service/
preferred date+time) violates the partial unique index
idx_waitlist_active_unique, which postgres-js surfaces as SQLSTATE
23505. The POST /portal/waitlist handler did not catch it, so the
duplicate returned a generic 500. Catch 23505 specifically and return
409 with a friendly message; unrelated errors still surface as 500 and
the first insert still returns 201.

Adds integration coverage: 201 first insert, 409 duplicate, 500 unrelated.
Flea Flicker added 1 commit 2026-06-08 23:48:04 +00:00
ci: re-trigger checks (transient Alpine CDN DNS flake in docker build)
CI / Test (pull_request) Successful in 26s
CI / Lint & Typecheck (pull_request) Successful in 32s
CI / Build & Push Docker Images (pull_request) Successful in 1m5s
7bdb96631f
Flea Flicker merged commit 6702086c7b into dev 2026-06-08 23:50:22 +00:00
Sign in to join this conversation.