Skip to content

Commit 395e12f

Browse files
slayerjainclaude
andcommitted
fix(sap-demo): /api/v1/audit returns 200 on empty audit table
GET /api/v1/audit returned 500 because the route wasn't registered and Spring's fallback NoResourceFoundException was caught by the generic @ExceptionHandler(Exception.class), masquerading as an internal server error. The intuitive audit-centric path should just work, and unmapped routes in general should 404 cleanly rather than 500. Changes: * AuditController exposes GET /api/v1/audit as the canonical route, keeping GET /api/v1/customers/recent-views as a back-compat alias. Null-safes the result list so an empty audit table still returns `{"items":[],"count":0}` rather than falling through to Map.of's NPE-on-null. * GlobalExceptionHandler adds an explicit NoResourceFoundException handler that returns 404 with an RFC 7807 body, so typos / probing no longer surface as 500s. Reproduction: curl -i http://localhost:8080/api/v1/audit → HTTP 500 + "An unexpected error occurred" Log: NoResourceFoundException: No static resource api/v1/audit. Root cause: Controller mapped at /api/v1/customers/recent-views; no route for /api/v1/audit; Spring's static-resource fallback throws NoResourceFoundException which the catch-all Exception handler treated as a 500 (should be 404). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 70c76d2 commit 395e12f

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

sap-demo-java/src/main/java/com/tricentisdemo/sap/customer360/web/AuditController.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414
import java.util.List;
1515
import java.util.Map;
1616

17+
/**
18+
* Postgres-only access-audit read surface — no SAP call on this path.
19+
*
20+
* <p>Exposes two equivalent routes for the same underlying query so both
21+
* "audit-centric" consumers ({@code /api/v1/audit}) and the legacy
22+
* customer-centric UI ({@code /api/v1/customers/recent-views}) resolve
23+
* without the generic 500-from-NoResourceFoundException trap.
24+
*/
1725
@RestController
18-
@RequestMapping("/api/v1/customers")
1926
@Tag(name = "Audit", description = "Service-level access audit (Postgres read)")
2027
public class AuditController {
2128

@@ -29,10 +36,13 @@ public AuditController(AuditService audit) {
2936

3037
@Operation(summary = "Most recent 50 audit events across all customers",
3138
description = "Postgres-only — no SAP call. Useful for compliance/ops dashboards.")
32-
@GetMapping("/recent-views")
39+
@GetMapping({"/api/v1/audit", "/api/v1/customers/recent-views"})
3340
public ResponseEntity<Map<String, Object>> recent() {
34-
log.info("GET /customers/recent-views");
41+
log.info("GET audit recent-views");
3542
List<AuditEvent> events = audit.recent();
43+
if (events == null) {
44+
events = List.of();
45+
}
3646
return ResponseEntity.ok(Map.of(
3747
"items", events,
3848
"count", events.size()

sap-demo-java/src/main/java/com/tricentisdemo/sap/customer360/web/GlobalExceptionHandler.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.springframework.web.bind.MethodArgumentNotValidException;
1818
import org.springframework.web.bind.annotation.ExceptionHandler;
1919
import org.springframework.web.bind.annotation.RestControllerAdvice;
20+
import org.springframework.web.servlet.resource.NoResourceFoundException;
2021

2122
/**
2223
* Converts uncaught exceptions into RFC 7807 problem responses.
@@ -77,6 +78,23 @@ public ResponseEntity<ProblemResponse> handleValidation(Exception ex, HttpServle
7778
.body(body);
7879
}
7980

81+
@ExceptionHandler(NoResourceFoundException.class)
82+
public ResponseEntity<ProblemResponse> handleNoRoute(NoResourceFoundException ex,
83+
HttpServletRequest req) {
84+
// Spring's default flow routes an unmatched URL through the static
85+
// resource handler, which then throws NoResourceFoundException.
86+
// Without this handler it falls into the generic Exception catch
87+
// and surfaces as a 500 — confusing for clients probing nearby
88+
// routes (e.g. GET /api/v1/audit when they meant
89+
// /api/v1/customers/recent-views).
90+
log.info("Unmapped route path={}", req.getRequestURI());
91+
ProblemResponse body = baseProblem(HttpStatus.NOT_FOUND, "Not Found",
92+
"No handler is registered for " + req.getRequestURI(), req);
93+
return ResponseEntity.status(HttpStatus.NOT_FOUND)
94+
.headers(commonHeaders(null))
95+
.body(body);
96+
}
97+
8098
@ExceptionHandler(DataAccessException.class)
8199
public ResponseEntity<ProblemResponse> handleDb(DataAccessException ex, HttpServletRequest req) {
82100
log.warn("database error surfaced to caller", ex);

0 commit comments

Comments
 (0)