Refactor: Standardisation complète de l'architecture REST

🔧 RESTRUCTURATION
- UserResource déplacé de adapter.http vers application.rest
- FournisseurResource déplacé vers application.rest
- Suppression des contrôleurs obsolètes (presentation.controller)
- Suppression de MaterielFournisseurService en doublon

📝 STANDARDISATION DOCUMENTATION
- Annotations OpenAPI uniformes (@Operation, @APIResponse, @Parameter)
- Descriptions concises et cohérentes pour tous les endpoints
- Codes de réponse HTTP standards (200, 201, 400, 404, 500)

🛠️ ENDPOINTS USERS STANDARDISÉS
- GET /api/v1/users - Liste tous les utilisateurs
- GET /api/v1/users/{id} - Détails d'un utilisateur
- GET /api/v1/users/stats - Statistiques globales
- GET /api/v1/users/count - Comptage
- GET /api/v1/users/pending - Utilisateurs en attente
- POST /api/v1/users - Création
- PUT /api/v1/users/{id} - Mise à jour
- DELETE /api/v1/users/{id} - Suppression
- POST /api/v1/users/{id}/approve - Approbation
- POST /api/v1/users/{id}/reject - Rejet
- PUT /api/v1/users/{id}/status - Changement de statut
- PUT /api/v1/users/{id}/role - Changement de rôle

⚠️ GESTION D'ERREURS
- Format uniforme: Map.of("error", "message")
- Codes HTTP cohérents avec les autres ressources
- Validation des entrées standardisée

 VALIDATION
- Compilation réussie: mvn clean compile -DskipTests
- Pattern conforme aux autres ressources (PhaseTemplate, Fournisseur)
- Documentation OpenAPI/Swagger complète et cohérente
This commit is contained in:
dahoud
2025-10-23 10:43:32 +00:00
parent de943a4a29
commit fba7666268
19 changed files with 1445 additions and 2651 deletions

View File

@@ -1,4 +1,4 @@
package dev.lions.btpxpress.adapter.http;
package dev.lions.btpxpress.application.rest;
import dev.lions.btpxpress.application.service.UserService;
import dev.lions.btpxpress.domain.core.entity.User;
@@ -13,6 +13,7 @@ import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
@@ -23,8 +24,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Resource REST pour la gestion des utilisateurs - Architecture 2025 SÉCURITÉ: Accès restreint aux
* administrateurs
* API REST pour la gestion des utilisateurs BTP
* Expose les fonctionnalités de création, consultation et administration des utilisateurs
*/
@Path("/api/v1/users")
@Produces(MediaType.APPLICATION_JSON)
@@ -38,23 +39,21 @@ public class UserResource {
@Inject UserService userService;
// === ENDPOINTS DE CONSULTATION ===
// ===================================
// CONSULTATION DES UTILISATEURS
// ===================================
@GET
@Operation(summary = "Récupérer tous les utilisateurs")
@APIResponse(responseCode = "200", description = "Liste des utilisateurs récupérée avec succès")
@Operation(summary = "Récupère tous les utilisateurs")
@APIResponse(responseCode = "200", description = "Liste des utilisateurs")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Accès refusé - droits administrateur requis")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getAllUsers(
@Parameter(description = "Numéro de page (0-indexed)") @QueryParam("page") @DefaultValue("0")
int page,
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
int size,
@Parameter(description = "Numéro de page (0-indexed)") @QueryParam("page") @DefaultValue("0") int page,
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20") int size,
@Parameter(description = "Terme de recherche") @QueryParam("search") String search,
@Parameter(description = "Filtrer par rôle") @QueryParam("role") String role,
@Parameter(description = "Filtrer par statut") @QueryParam("status") String status,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Filtrer par statut") @QueryParam("status") String status) {
try {
List<User> users;
@@ -74,28 +73,22 @@ public class UserResource {
List<UserResponse> userResponses = users.stream().map(this::toUserResponse).toList();
return Response.ok(userResponses).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la récupération des utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la récupération des utilisateurs: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la récupération des utilisateurs"))
.build();
}
}
@GET
@Path("/{id}")
@Operation(summary = "Récupérer un utilisateur par ID")
@APIResponse(responseCode = "200", description = "Utilisateur récupéré avec succès")
@Operation(summary = "Récupère un utilisateur par ID")
@APIResponse(responseCode = "200", description = "Utilisateur trouvé")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getUserById(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id) {
try {
UUID userId = UUID.fromString(id);
return userService
@@ -103,20 +96,16 @@ public class UserResource {
.map(user -> Response.ok(toUserResponse(user)).build())
.orElse(
Response.status(Response.Status.NOT_FOUND)
.entity("Utilisateur non trouvé avec l'ID: " + id)
.entity(Map.of("error", "Utilisateur non trouvé avec l'ID: " + id))
.build());
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ID d'utilisateur invalide: " + id)
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "ID d'utilisateur invalide: " + id))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la récupération de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la récupération de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la récupération de l'utilisateur"))
.build();
}
}
@@ -181,80 +170,65 @@ public class UserResource {
@GET
@Path("/stats")
@Operation(summary = "Obtenir les statistiques des utilisateurs")
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès")
@Operation(summary = "Récupère les statistiques des utilisateurs")
@APIResponse(responseCode = "200", description = "Statistiques des utilisateurs")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response getUserStats(
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
public Response getUserStats() {
try {
Object stats = userService.getStatistics();
return Response.ok(stats).build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.build();
} catch (Exception e) {
logger.error("Erreur lors de la génération des statistiques utilisateurs", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la génération des statistiques: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la génération des statistiques"))
.build();
}
}
// === ENDPOINTS DE GESTION ===
// ===================================
// GESTION DES UTILISATEURS
// ===================================
@POST
@Operation(summary = "Créer un nouvel utilisateur")
@Operation(summary = "Crée un nouvel utilisateur")
@APIResponse(responseCode = "201", description = "Utilisateur créé avec succès")
@APIResponse(responseCode = "400", description = "Données invalides")
@APIResponse(responseCode = "409", description = "Email déjà utilisé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response createUser(
@Parameter(description = "Données du nouvel utilisateur") @Valid @NotNull
CreateUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Données du nouvel utilisateur") @Valid @NotNull CreateUserRequest request) {
try {
User user =
userService.createUser(
request.email,
request.password,
request.nom,
request.prenom,
request.role,
request.status);
User user = userService.createUser(
request.email,
request.password,
request.nom,
request.prenom,
request.role,
request.status);
return Response.status(Response.Status.CREATED).entity(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Données invalides: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la création de l'utilisateur", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la création de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la création de l'utilisateur"))
.build();
}
}
@PUT
@Path("/{id}")
@Operation(summary = "Modifier un utilisateur")
@APIResponse(responseCode = "200", description = "Utilisateur modifié avec succès")
@Operation(summary = "Met à jour un utilisateur")
@APIResponse(responseCode = "200", description = "Utilisateur mis à jour avec succès")
@APIResponse(responseCode = "400", description = "Données invalides")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouvelles données utilisateur") @Valid @NotNull
UpdateUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Nouvelles données utilisateur") @Valid @NotNull UpdateUserRequest request) {
try {
UUID userId = UUID.fromString(id);
User user = userService.updateUser(userId, request.nom, request.prenom, request.email);
@@ -262,32 +236,26 @@ public class UserResource {
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Données invalides: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la modification de l'utilisateur"))
.build();
}
}
@PUT
@Path("/{id}/status")
@Operation(summary = "Modifier le statut d'un utilisateur")
@APIResponse(responseCode = "200", description = "Statut modifié avec succès")
@Operation(summary = "Met à jour le statut d'un utilisateur")
@APIResponse(responseCode = "200", description = "Statut mis à jour avec succès")
@APIResponse(responseCode = "400", description = "Statut invalide")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUserStatus(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouveau statut") @Valid @NotNull UpdateStatusRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Nouveau statut") @Valid @NotNull UpdateStatusRequest request) {
try {
UUID userId = UUID.fromString(id);
UserStatus status = UserStatus.valueOf(request.status.toUpperCase());
@@ -297,32 +265,26 @@ public class UserResource {
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Statut invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Statut invalide: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification du statut utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification du statut: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la modification du statut"))
.build();
}
}
@PUT
@Path("/{id}/role")
@Operation(summary = "Modifier le rôle d'un utilisateur")
@APIResponse(responseCode = "200", description = "Rôle modifié avec succès")
@Operation(summary = "Met à jour le rôle d'un utilisateur")
@APIResponse(responseCode = "200", description = "Rôle mis à jour avec succès")
@APIResponse(responseCode = "400", description = "Rôle invalide")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response updateUserRole(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Nouveau rôle") @Valid @NotNull UpdateRoleRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Nouveau rôle") @Valid @NotNull UpdateRoleRequest request) {
try {
UUID userId = UUID.fromString(id);
UserRole role = UserRole.valueOf(request.role.toUpperCase());
@@ -332,30 +294,24 @@ public class UserResource {
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Rôle invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Rôle invalide: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la modification du rôle utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la modification du rôle: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la modification du rôle"))
.build();
}
}
@POST
@Path("/{id}/approve")
@Operation(summary = "Approuver un utilisateur en attente")
@Operation(summary = "Approuve un utilisateur en attente")
@APIResponse(responseCode = "200", description = "Utilisateur approuvé avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response approveUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id) {
try {
UUID userId = UUID.fromString(id);
User user = userService.approveUser(userId);
@@ -363,62 +319,50 @@ public class UserResource {
return Response.ok(toUserResponse(user)).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Données invalides: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de l'approbation de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de l'approbation de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de l'approbation de l'utilisateur"))
.build();
}
}
@POST
@Path("/{id}/reject")
@Operation(summary = "Rejeter un utilisateur en attente")
@Operation(summary = "Rejette un utilisateur en attente")
@APIResponse(responseCode = "200", description = "Utilisateur rejeté avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response rejectUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Raison du rejet") @Valid @NotNull RejectUserRequest request,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "Raison du rejet") @Valid @NotNull RejectUserRequest request) {
try {
UUID userId = UUID.fromString(id);
userService.rejectUser(userId, request.reason);
return Response.ok().entity("Utilisateur rejeté avec succès").build();
return Response.ok(Map.of("message", "Utilisateur rejeté avec succès")).build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Données invalides: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "Données invalides: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors du rejet de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors du rejet de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors du rejet de l'utilisateur"))
.build();
}
}
@DELETE
@Path("/{id}")
@Operation(summary = "Supprimer un utilisateur")
@Operation(summary = "Supprime un utilisateur")
@APIResponse(responseCode = "204", description = "Utilisateur supprimé avec succès")
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé")
@APIResponse(responseCode = "403", description = "Accès refusé")
public Response deleteUser(
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id,
@Parameter(description = "Token d'authentification") @HeaderParam("Authorization")
String authorizationHeader) {
@Parameter(description = "ID de l'utilisateur") @PathParam("id") String id) {
try {
UUID userId = UUID.fromString(id);
userService.deleteUser(userId);
@@ -426,16 +370,12 @@ public class UserResource {
return Response.noContent().build();
} catch (IllegalArgumentException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ID invalide: " + e.getMessage())
.build();
} catch (SecurityException e) {
return Response.status(Response.Status.FORBIDDEN)
.entity("Accès refusé: " + e.getMessage())
.entity(Map.of("error", "ID invalide: " + e.getMessage()))
.build();
} catch (Exception e) {
logger.error("Erreur lors de la suppression de l'utilisateur {}", id, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Erreur lors de la suppression de l'utilisateur: " + e.getMessage())
.entity(Map.of("error", "Erreur lors de la suppression de l'utilisateur"))
.build();
}
}