Skip to content

fix(admin): 409 (not 500) on duplicate menu / group code#27

Merged
jlc488 merged 1 commit into
mainfrom
fix/menu-group-duplicate-conflict
May 30, 2026
Merged

fix(admin): 409 (not 500) on duplicate menu / group code#27
jlc488 merged 1 commit into
mainfrom
fix/menu-group-duplicate-conflict

Conversation

@jlc488

@jlc488 jlc488 commented May 30, 2026

Copy link
Copy Markdown
Contributor

Consistency gap found by auditing every create endpoint after the menu duplicate surfaced during integration testing — you asked me to check whether other resources had the same problem, and two did.

The gap

GlobalExceptionHandler maps IllegalStateException → 409 CONFLICT, and User / Role / Permission / Tenant services all do a findBy…isPresent() pre-check and throw it on a duplicate. But MenuAdminService.create and GroupService.create had no such check — they went straight to repository.save(), hit the uq_platform_{menu,group}_tenant_code unique constraint, and bubbled a DataIntegrityViolationException that no handler caught → 500 instead of 409.

The fix

Both tables already declare the unique constraint; this just adds the friendly pre-check the other four resources already had:

  • JpaPlatformGroupRepository / JpaPlatformMenuRepository: new findByTenantIdAndCode(tenantId, code).
  • GroupService.create / MenuAdminService.create: throw IllegalStateException("… already exists …") when a row with the same (tenantId, code) exists.

Verified end-to-end (running sample-app)

POST /menus  {code: dup.menu.test}  → 201 then 409
POST /groups {code: dup-grp}        → 201 then 409
POST /roles  {code: PLATFORM_ADMIN} → 409 (unchanged, sanity check)

Known limitation (documented, not fixed)

There's still a narrow race between the pre-check and the insert under concurrent creates — the DB constraint is the real guard and would 500 in that rare window. Catching DataIntegrityViolationException in GlobalExceptionHandler would close it, but I left that out to keep the change minimal and consistent with the existing four services, which share the same theoretical race. Worth a follow-up if you want belt-and-suspenders.

Context

5th item from integration verification. The full audit of create endpoints confirmed only menu + group were affected; the not-found path (IllegalArgumentException → 400) is uniform across all resources and was left as-is.

Consistency gap found by auditing every create endpoint after the
menu duplicate surfaced during integration testing.

GlobalExceptionHandler maps IllegalStateException -> 409 CONFLICT,
and User / Role / Permission / Tenant services all do a
findBy...isPresent() pre-check and throw it on a duplicate. But
MenuAdminService.create and GroupService.create had no such check —
they went straight to repository.save(), hit the
uq_platform_{menu,group}_tenant_code unique constraint, and bubbled
a DataIntegrityViolationException that no handler caught, producing
a 500 instead of a clean 409.

Both tables already declare the unique constraint, so this only adds
the friendly pre-check that the other four resources already had:

- JpaPlatformGroupRepository / JpaPlatformMenuRepository:
  new findByTenantIdAndCode(tenantId, code).
- GroupService.create / MenuAdminService.create:
  throw IllegalStateException("... already exists ...") when a row
  with the same (tenantId, code) exists.

Verified end-to-end against the running sample-app:
  POST /menus  {code: dup.menu.test} -> 201 then 409
  POST /groups {code: dup-grp}       -> 201 then 409
  POST /roles  {code: PLATFORM_ADMIN}-> 409 (unchanged, sanity)

Note: there's still a small race between the pre-check and the
insert under concurrent creates — the DB constraint is the real
guard and would 500 in that rare window. Making that path also
return 409 would mean catching DataIntegrityViolationException in
GlobalExceptionHandler; left out here to keep the change minimal
and consistent with the existing four services, which have the
same theoretical race.
@jlc488
jlc488 merged commit e2348bf into main May 30, 2026
1 check passed
@jlc488
jlc488 deleted the fix/menu-group-duplicate-conflict branch May 30, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant