Skip to content

feat(backend): global error handling & request validation layer (RFC … - #34

Merged
meshackyaro merged 2 commits into
workman-labs:developmentfrom
CmxTop:feature/global-error-handling-rfc7807
Jul 25, 2026
Merged

feat(backend): global error handling & request validation layer (RFC …#34
meshackyaro merged 2 commits into
workman-labs:developmentfrom
CmxTop:feature/global-error-handling-rfc7807

Conversation

@CmxTop

@CmxTop CmxTop commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Replace the ad-hoc {error, success} exception contract with RFC 7807 (application/problem+json) across the API: GlobalExceptionHandler now returns ProblemDetail (type/title/status/detail + an errors extension for field/parameter validation), and the security filter-chain's 401/403 handlers render the same shape since they run outside the DispatcherServlet.

Also closes the bean-validation gaps: every @RequestBody DTO is now validated with @Valid, request DTOs gained missing constraints, and @RequestParam/@PathVariable identifiers are validated via @validated + jakarta.validation on ClientController/SkilledWorkerController.

Closes #25

…7807)

Replace the ad-hoc {error, success} exception contract with RFC 7807
(application/problem+json) across the API: GlobalExceptionHandler now
returns ProblemDetail (type/title/status/detail + an `errors` extension
for field/parameter validation), and the security filter-chain's 401/403
handlers render the same shape since they run outside the DispatcherServlet.

Also closes the bean-validation gaps: every @RequestBody DTO is now
validated with @Valid, request DTOs gained missing constraints, and
@RequestParam/@PathVariable identifiers are validated via @validated +
jakarta.validation on ClientController/SkilledWorkerController.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@meshackyaro

Copy link
Copy Markdown
Contributor

Replace the ad-hoc {error, success} exception contract with RFC 7807 (application/problem+json) across the API: GlobalExceptionHandler now returns ProblemDetail (type/title/status/detail + an errors extension for field/parameter validation), and the security filter-chain's 401/403 handlers render the same shape since they run outside the DispatcherServlet.

Also closes the bean-validation gaps: every @RequestBody DTO is now validated with @Valid, request DTOs gained missing constraints, and @RequestParam/@PathVariable identifiers are validated via @validated + jakarta.validation on ClientController/SkilledWorkerController.

Closes #25

Thanks — this is a great, well-scoped improvement. The RFC 7807 move + the validation cleanup makes the API more consistent and safer for consumers. I’ll merge once a few small, important issues are addressed (see below).

Required changes (please address)

  1. Explicit HTTP status in exception handlers
    • Several handlers return ProblemDetail directly. Please return a ResponseEntity and set the HTTP status explicitly (e.g. return ResponseEntity.status(HttpStatus.CONFLICT).body(problemDetail)). Relying on implicit mapping can be fragile across Spring versions and middleware.
  2. Consistent timestamp representation
    • ProblemDetails.of(...) sets a timestamp property to an Instant (object) while asMap(...) writes Instant.toString(). Use a single canonical representation (ISO-8601 String) in both places so clients always get the same type.
  3. Date/time request param parsing
    • Controller methods that accept LocalDateTime via @RequestParam (e.g. scheduleAvailability in ClientController) should specify @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) or accept and parse a String so parsing is predictable for clients. Please apply this to all date/time request params.
  4. Make sure @validated is present wherever method-level parameter constraints are used
    • You added @validated to ClientController and SkilledWorkerController — please scan for other controllers that use @NotNull/@notblank on @RequestParam/@PathVariable (or any method-level constraint) and add @validated at the class level so ConstraintViolationException is raised and handled uniformly.
  5. Keep the security handlers and controllers identical in shape
    • ProblemDetails.asMap(...) currently doesn’t accept an errors map. If you want filter-chain handlers (401/403) to ever include field-level errors, add an overloaded asMap(...) that accepts an optional errors Map so both code paths produce precisely the same JSON keys.
  6. Tests & CI
    • Great tests — please run the full test suite and make sure CI is green. Search for any remaining assertions expecting the old {success} boolean and update them. Once tests are passing and the few items above are fixed I’ll merge.

Optional / suggestions

  • Consider making ProblemDetails.TYPE_BASE configurable via application properties so environments (staging/prod) can expose appropriate canonical URIs.
  • Add a short changelog / release note describing the new error contract and the validation hardening so API consumers are aware of the behavioral change (this is potentially breaking for some clients).
  • If you maintain an OpenAPI spec or API docs, add the new application/problem+json responses there.

Next steps / merge plan

  • Please push a follow-up commit addressing the required changes above and ensure CI passes.
  • After that, I’ll give a final quick pass (or merge if CI is clean and changes are small).

Meanwhile, this is a high-quality PR and moves the API in the right direction.

Get this done and it'll be ready for merge.

- All GlobalExceptionHandler methods now return ResponseEntity<ProblemDetail>
  with the HTTP status set explicitly, instead of relying on the framework to
  infer it from ProblemDetail's own status field.
- ProblemDetails.of and .asMap now both write timestamp as the same ISO-8601
  string, instead of an Instant object on one path and a string on the other.
- Added an asMap overload that accepts an optional errors map, so the
  security filter-chain handlers (401/403) can produce the identical JSON
  shape as GlobalExceptionHandler if a validation-flavoured 401/403 ever
  needs field-level detail.
- scheduleAvailability's LocalDateTime @RequestParams now declare
  @DateTimeFormat(iso = ISO.DATE_TIME) for predictable parsing.
- Made the problem `type` base URI configurable via
  guildworkman.problem-details.type-base (PROBLEM_TYPE_BASE env var), wired
  through a small ProblemDetailsConfig bean since ProblemDetails is a static
  utility shared by non-Spring-managed call sites.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@CmxTop

CmxTop commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

@meshackyaro I pushed with all required fixes: explicit ResponseEntity status on every handler, consistent ISO-8601 timestamps, @DateTimeFormat on the date params, confirmed @validated coverage is complete, and an errors-aware asMap overload for the security handlers. Also made the problem type base URI configurable (optional item). No stale {success} assertions found in tests.

@meshackyaro meshackyaro left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 6 required changes addressed in follow-up commit; RFC 7807 compliance complete with comprehensive validation hardening.

Well done @CmxTop and thank you for contributing to Guildworkman

@meshackyaro
meshackyaro merged commit 81e143e into workman-labs:development Jul 25, 2026
1 check passed
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.

Global Error Handling & Request Validation Layer (RFC 7807)

2 participants