Refactoring - Version stable
This commit is contained in:
@@ -2,6 +2,9 @@ package dev.lions.unionflow.server.exception;
|
|||||||
|
|
||||||
import dev.lions.unionflow.server.service.SystemLoggingService;
|
import dev.lions.unionflow.server.service.SystemLoggingService;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.ws.rs.ForbiddenException;
|
||||||
|
import jakarta.ws.rs.NotAuthorizedException;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.WebApplicationException;
|
import jakarta.ws.rs.WebApplicationException;
|
||||||
import jakarta.ws.rs.core.Context;
|
import jakarta.ws.rs.core.Context;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
@@ -33,12 +36,19 @@ public class GlobalExceptionMapper implements ExceptionMapper<Throwable> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response toResponse(Throwable exception) {
|
public Response toResponse(Throwable exception) {
|
||||||
// Logger l'exception dans les logs applicatifs
|
|
||||||
log.error("Unhandled exception", exception);
|
|
||||||
|
|
||||||
// Déterminer le code HTTP
|
// Déterminer le code HTTP
|
||||||
int statusCode = determineStatusCode(exception);
|
int statusCode = determineStatusCode(exception);
|
||||||
|
|
||||||
|
// Les exceptions métier 4xx (404, 401, 403) sont des cas normaux :
|
||||||
|
// on les logue en DEBUG/WARN, sans stack trace et sans persister en system_logs.
|
||||||
|
if (isExpectedClientError(exception)) {
|
||||||
|
log.debug("Expected client error [{}]: {}", statusCode, exception.getMessage());
|
||||||
|
return buildErrorResponse(exception, statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour toute autre exception (5xx ou inattendue) : log ERROR + persistance
|
||||||
|
log.error("Unhandled exception", exception);
|
||||||
|
|
||||||
// Récupérer l'endpoint (safe pour les tests unitaires)
|
// Récupérer l'endpoint (safe pour les tests unitaires)
|
||||||
String endpoint = "unknown";
|
String endpoint = "unknown";
|
||||||
try {
|
try {
|
||||||
@@ -72,6 +82,17 @@ public class GlobalExceptionMapper implements ExceptionMapper<Throwable> {
|
|||||||
return buildErrorResponse(exception, statusCode);
|
return buildErrorResponse(exception, statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retourne {@code true} pour les exceptions 4xx qui représentent
|
||||||
|
* des cas métier attendus (ressource absente, accès refusé, session expirée).
|
||||||
|
* Ces exceptions ne doivent pas être loguées ERROR ni persistées.
|
||||||
|
*/
|
||||||
|
private boolean isExpectedClientError(Throwable exception) {
|
||||||
|
return exception instanceof NotFoundException
|
||||||
|
|| exception instanceof ForbiddenException
|
||||||
|
|| exception instanceof NotAuthorizedException;
|
||||||
|
}
|
||||||
|
|
||||||
private int determineStatusCode(Throwable exception) {
|
private int determineStatusCode(Throwable exception) {
|
||||||
if (exception instanceof WebApplicationException webAppException) {
|
if (exception instanceof WebApplicationException webAppException) {
|
||||||
return webAppException.getResponse().getStatus();
|
return webAppException.getResponse().getStatus();
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import com.fasterxml.jackson.core.JsonParser;
|
|||||||
import io.quarkus.test.junit.QuarkusTest;
|
import io.quarkus.test.junit.QuarkusTest;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.ws.rs.BadRequestException;
|
import jakarta.ws.rs.BadRequestException;
|
||||||
|
import jakarta.ws.rs.ForbiddenException;
|
||||||
|
import jakarta.ws.rs.NotAuthorizedException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
@@ -67,7 +69,7 @@ class GlobalExceptionMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("NotFoundException → 404")
|
@DisplayName("NotFoundException → 404 (cas métier attendu : pas de log ERROR, pas de persistance)")
|
||||||
void mapRuntimeException_notFound_returns404() {
|
void mapRuntimeException_notFound_returns404() {
|
||||||
Response r = globalExceptionMapper.toResponse(
|
Response r = globalExceptionMapper.toResponse(
|
||||||
new jakarta.ws.rs.NotFoundException("Ressource introuvable"));
|
new jakarta.ws.rs.NotFoundException("Ressource introuvable"));
|
||||||
@@ -77,6 +79,27 @@ class GlobalExceptionMapperTest {
|
|||||||
assertThat(body.get("error")).isEqualTo("Ressource introuvable");
|
assertThat(body.get("error")).isEqualTo("Ressource introuvable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("ForbiddenException → 403 (cas métier attendu : pas de log ERROR)")
|
||||||
|
void mapForbiddenException_returns403() {
|
||||||
|
Response r = globalExceptionMapper.toResponse(new ForbiddenException("Accès refusé"));
|
||||||
|
assertThat(r.getStatus()).isEqualTo(403);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
|
||||||
|
assertThat(body.get("error")).isEqualTo("Accès refusé");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("NotAuthorizedException → 401 (cas métier attendu : pas de log ERROR)")
|
||||||
|
void mapNotAuthorizedException_returns401() {
|
||||||
|
Response r = globalExceptionMapper.toResponse(
|
||||||
|
new NotAuthorizedException("Session expirée", "Bearer"));
|
||||||
|
assertThat(r.getStatus()).isEqualTo(401);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
java.util.Map<String, Object> body = (java.util.Map<String, Object>) r.getEntity();
|
||||||
|
assertThat(body.get("error")).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("WebApplicationException 400 avec message non vide → 400")
|
@DisplayName("WebApplicationException 400 avec message non vide → 400")
|
||||||
void mapRuntimeException_webApp4xx_withMessage_returns4xx() {
|
void mapRuntimeException_webApp4xx_withMessage_returns4xx() {
|
||||||
|
|||||||
Reference in New Issue
Block a user