Skip to content

feat(i18n): localize servicedesk errors + guard against un-keyed throws (#16 Phase-2 close-out)#90

Merged
WolfTasks merged 1 commit into
mainfrom
worktree-backend-i18n-phase2-session5
Jul 21, 2026
Merged

feat(i18n): localize servicedesk errors + guard against un-keyed throws (#16 Phase-2 close-out)#90
WolfTasks merged 1 commit into
mainfrom
worktree-backend-i18n-phase2-session5

Conversation

@WolfTasks

Copy link
Copy Markdown
Owner

Final session of the #16 backend-i18n Phase-2 API-error sweep (Task 9 servicedesk + sweep close-out). Single commit, TDD-ordered. Branches off clean main — no stacking.

What changed

Localizes the last remaining module + fixes latent 500s. Both servicedesk REST controllers threw ?: error(...) (unhandled IllegalStateExceptionHTTP 500 for ordinary "unknown project / desk not enabled" conditions) and English-only ResponseStatusException. Converted to the project's keyed domain exceptions:

  • ServiceDeskController.kt / IncidentController.kt:
    • ?: error("Project not found…" / "Service desk not enabled…")NotFoundException.keyed("project.notFound" | "serviceDesk.notEnabled", key)500 → localized 404
    • ResponseStatusException(BAD_REQUEST, …)BadRequestException.keyed("serviceDesk.invalidPriority" | "incident.invalidSeverity", …)stays 400, now localized body
    • removed now-unused ResponseStatusException import; kept HttpStatus (still used by @ResponseStatus)
  • New catalog keys (en base messages.properties + de messages_de.properties, \uXXXX-escaped; project.notFound reused): serviceDesk.notEnabled, serviceDesk.invalidPriority, incident.invalidSeverity (the last doubled ''{0}'' per the MessageFormat rule).

Durable regression guard — NoUnkeyedUserFacingThrowTest. A static gate that fails CI on any un-keyed user-facing throw — bare OR fully-qualified (throw com.taskowolf…NotFoundException("…")) — plus free-text orElseThrow, ?: error(, ResponseStatusException, AccessDeniedException. Allowlists exactly the 2 intentional internal invariants (SsoController clientSecret, OidcUserProvisioningService OIDC-no-email). Closes the FQ-prefix blind spot that let an un-keyed AttachmentController throw slip through the earlier sweep. Has an anti-vacuous check (filesScanned > 0).

Tests

  • ServiceDeskMessagesTest — en/de resolution for the 3 new keys (proves the ''{0}'' doubling).
  • ServiceDeskErrorLocalizationTest (MockMvc) — unknown project → 404 localized de, desk-not-enabled → 404 localized de, invalid priority → 400 localized, invalid severity → 400 localized.
  • Full suite 373/373 green; all three static gates green (MessagesParityTest, KeyedReferenceIntegrityTest, NoUnkeyedUserFacingThrowTest).
  • TDD: guard's RED phase listed exactly the 11 servicedesk sites (no false positives); web test RED was 500/unlocalized.

Sweep close-out verification

The plan's widened FQ-blind-spot audit was run against the full src/main/kotlin tree and is clean everywhere except the 2 allowlisted invariants → the #16 Phase-2 API-error sweep is complete.

Reviews

Task review: Approved (0 Critical/Important). Final whole-branch review (opus): Ready to merge: Yes (0 Critical/Important). 3 Minor findings all inherited/pre-existing patterns, correctly deferred.

Handoff (owed by Wolfgang, after merge — out of scope here)

  • Manual DE/EN browser smoke: GET a service desk on a project that has none → German 404 (not 500); switch to English → English body; confirm Accept-Language in the Network tab.
  • The v1.0.x release.
  • Frontend note: GET …/service-desk now returns 404 (not 500) when the desk isn't enabled — treat that as the "not enabled" state. Both project-not-found and not-enabled share code NOT_FOUND (distinguishable by message only).
  • Phase 3 (emails + notifications, recipient-locale rendering) remains open.

🤖 Generated with Claude Code

Task 9 of the #16 Phase-2 sweep: 6x 500->404 (project/service-desk not-found),
2x ResponseStatusException->BadRequestException.keyed (invalid priority/severity,
now localized 400). New keys serviceDesk.notEnabled/invalidPriority +
incident.invalidSeverity (en/de). Adds NoUnkeyedUserFacingThrowTest, a static
gate that fails on any un-keyed user-facing throw (bare OR fully-qualified) —
closing the FQ-prefix blind spot that hid the AttachmentController throw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@WolfTasks
WolfTasks merged commit 1dab869 into main Jul 21, 2026
11 checks passed
@WolfTasks
WolfTasks deleted the worktree-backend-i18n-phase2-session5 branch July 21, 2026 17:10
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