Skip to content

UNOMI-928: Improve REST API error handling with dedicated exception mappers#771

Merged
sergehuber merged 6 commits into
masterfrom
UNOMI-928-rest-error-handling
Jun 11, 2026
Merged

UNOMI-928: Improve REST API error handling with dedicated exception mappers#771
sergehuber merged 6 commits into
masterfrom
UNOMI-928-rest-error-handling

Conversation

@sergehuber

Copy link
Copy Markdown
Contributor

JIRA: UNOMI-928Improve REST API error handling with dedicated exception mappers and enhanced logging

Base: master (standalone backport — not stacked)

Summary

Backport REST error-handling improvements from unomi-3-dev so Jackson deserialization failures are reported as client errors (HTTP 400) instead of leaking as HTTP 500, with sanitized request-context logging and consistent JSON error bodies.

What changed

  • New JsonMappingExceptionMapper — maps JsonMappingException400 {"errorMessage":"badRequest"}
  • New InternalServerErrorExceptionMapper — maps InternalServerErrorException; downgrades to 400 badRequest when root cause is JSON deserialization (JsonMappingException / JsonParseException), otherwise 500 internalServerError with detailed sanitized logging
  • Updated RuntimeExceptionMapper — enriched logging (request method/URI/query, root cause, WARN vs ERROR by cause type); response remains 500 (JSON causes only affect log level, not status)
  • Refactor (DRY): shared AbstractRestExceptionMapper (request context, root cause, standard JSON responses) and LogSanitizer (log-injection defenses, length limits)
  • Tests: RestExceptionMapperTest (6) and LogSanitizerTest (7); adds junit-jupiter test dependency to rest/pom.xml

All mappers register via existing @Provider + @Component(service = ExceptionMapper.class) — no blueprint/feature wiring changes.

Behavior notes for reviewers

Exception / path HTTP status Response body
JsonMappingException (direct) 400 {"errorMessage":"badRequest"}
InternalServerErrorException with JSON deserialization root cause 400 {"errorMessage":"badRequest"}
InternalServerErrorException with non-JSON root cause 500 {"errorMessage":"internalServerError"}
RuntimeException (including JSON root cause) 500 {"errorMessage":"internalServerError"}

Existing validation paths (InvalidRequestExceptionMapper, schema validation on custom deserializers) are unchanged and may still return different 400 bodies (plain text) before these mappers apply.

Out of scope / follow-up

  • ItemDeserializer robustness gap: malformed non-object Item JSON (e.g. "source":"string") still throws ClassCastException → 500 via RuntimeExceptionMapper. Present in unomi-3-dev too; candidate for a separate JIRA (not part of this backport).
  • No integration test added: public endpoints hit custom deserializers/schema validation first, so mapper contract is covered by unit tests instead.

Test plan

  • mvn -pl rest -am test -Dtest='RestExceptionMapperTest,LogSanitizerTest' -Dsurefire.failIfNoSpecifiedTests=false
  • Full ./build.sh / integration test suite (reviewer)
  • Manual smoke: POST malformed JSON to a REST endpoint that reaches Jackson directly → expect 400 badRequest where applicable

…appers

Add JsonMappingExceptionMapper and InternalServerErrorExceptionMapper so Jackson
deserialization failures return 400 badRequest instead of 500. Enrich
RuntimeExceptionMapper with sanitized request-context logging. Extract shared
logic into AbstractRestExceptionMapper and LogSanitizer. Add unit tests.
Backported from unomi-3-dev.

Copilot AI 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.

Pull request overview

Backports improved REST exception mapping and logging so Jackson deserialization failures are treated as client errors (HTTP 400) and REST error responses are more consistent and safer to log.

Changes:

  • Added dedicated exception mappers for JsonMappingException and InternalServerErrorException (with JSON-cause downgrade to 400).
  • Refactored shared behavior into AbstractRestExceptionMapper and introduced LogSanitizer for request-context logging sanitization.
  • Added JUnit 5 unit tests covering mapper status/body contracts and sanitizer behavior, plus a test dependency in rest/pom.xml.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
rest/src/main/java/org/apache/unomi/rest/exception/RuntimeExceptionMapper.java Refactors to shared base mapper and enhances request-context/root-cause logging.
rest/src/main/java/org/apache/unomi/rest/exception/AbstractRestExceptionMapper.java Adds shared helpers for request context, root-cause handling, JSON detection, and standard JSON error responses.
rest/src/main/java/org/apache/unomi/rest/exception/LogSanitizer.java Centralizes sanitization/truncation for request-derived log fields to mitigate log-injection/verbosity risks.
rest/src/main/java/org/apache/unomi/rest/exception/JsonMappingExceptionMapper.java New mapper: JsonMappingException → 400 with standard JSON error body.
rest/src/main/java/org/apache/unomi/rest/exception/InternalServerErrorExceptionMapper.java New mapper: InternalServerErrorException → 400 when JSON root-cause, else 500 with sanitized logging.
rest/src/test/java/org/apache/unomi/rest/exception/RestExceptionMapperTest.java New unit tests validating mapper status codes and JSON error bodies.
rest/src/test/java/org/apache/unomi/rest/exception/LogSanitizerTest.java New unit tests validating sanitizer behavior (control chars, truncation, whitelisting).
rest/pom.xml Adds JUnit Jupiter dependency for the new unit tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rest/src/main/java/org/apache/unomi/rest/exception/RuntimeExceptionMapper.java Outdated
Comment thread rest/pom.xml
sergehuber and others added 5 commits June 10, 2026 20:24
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The & separator was appended before the overflow guard, producing a
spurious & immediately before ...[more params]. Reorder the check so
the sentinel is appended directly after the last real parameter.

Add tests for queryString() and queryParameters() — the latter covers
the boundary at exactly 10 params and the truncation case at 11.
@sergehuber sergehuber merged commit a304a48 into master Jun 11, 2026
14 of 18 checks passed
@sergehuber sergehuber deleted the UNOMI-928-rest-error-handling branch June 11, 2026 15:27
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.

2 participants