package dev.lions.unionflow.client.service; import jakarta.ws.rs.core.Response; import org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper; public class RestClientExceptionMapper implements ResponseExceptionMapper { @Override public RuntimeException toThrowable(Response response) { int status = response.getStatus(); String reasonPhrase = response.getStatusInfo().getReasonPhrase(); java.util.logging.Logger logger = java.util.logging.Logger.getLogger(RestClientExceptionMapper.class.getName()); // Logger l'URL et le statut pour debugging try { if (response.getLocation() != null) { logger.severe("Erreur backend - URL: " + response.getLocation() + " - Status: " + status); } } catch (Exception e) { // Ignorer si on ne peut pas obtenir l'URL } // Lire le corps de la réponse pour plus de détails // SÉCURITÉ: Ne pas exposer les détails des erreurs serveur (5xx) au client String body = ""; boolean shouldIncludeBody = status >= 400 && status < 500; // Seulement pour erreurs client (4xx) if (shouldIncludeBody) { try { if (response.hasEntity()) { body = response.readEntity(String.class); logger.severe("Corps de la réponse (4xx): " + body); } } catch (Exception e) { body = "Impossible de lire le détail de l'erreur"; logger.warning("Impossible de lire le corps de la réponse: " + e.getMessage()); } } // Logger toutes les erreurs pour debugging logger.severe("Erreur backend - HTTP " + status + " (" + reasonPhrase + ")"); return switch (status) { case 400 -> new BadRequestException("Requête invalide: " + body); case 401 -> new UnauthorizedException("Non autorisé"); case 403 -> new ForbiddenException("Accès interdit"); case 404 -> new NotFoundException("Ressource non trouvée"); case 409 -> new ConflictException("Conflit: " + body); case 422 -> new UnprocessableEntityException("Données non valides: " + body); // SÉCURITÉ: Erreurs 5xx - Messages génériques sans détails backend case 500 -> new InternalServerErrorException("Erreur serveur interne. Veuillez réessayer ultérieurement."); case 502 -> new BadGatewayException("Service temporairement indisponible"); case 503 -> new ServiceUnavailableException("Service indisponible. Veuillez réessayer ultérieurement."); case 504 -> new GatewayTimeoutException("Délai d'attente dépassé. Veuillez réessayer."); default -> new UnknownHttpStatusException("Une erreur est survenue. Veuillez réessayer."); }; } // Classes d'exception personnalisées public static class BadRequestException extends RuntimeException { public BadRequestException(String message) { super(message); } } public static class UnauthorizedException extends RuntimeException { public UnauthorizedException(String message) { super(message); } } public static class ForbiddenException extends RuntimeException { public ForbiddenException(String message) { super(message); } } public static class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); } } public static class ConflictException extends RuntimeException { public ConflictException(String message) { super(message); } } public static class UnprocessableEntityException extends RuntimeException { public UnprocessableEntityException(String message) { super(message); } } public static class InternalServerErrorException extends RuntimeException { public InternalServerErrorException(String message) { super(message); } } public static class BadGatewayException extends RuntimeException { public BadGatewayException(String message) { super(message); } } public static class ServiceUnavailableException extends RuntimeException { public ServiceUnavailableException(String message) { super(message); } } public static class GatewayTimeoutException extends RuntimeException { public GatewayTimeoutException(String message) { super(message); } } public static class UnknownHttpStatusException extends RuntimeException { public UnknownHttpStatusException(String message) { super(message); } } }