From bf1e9e16d833936ce36a0fa4d41a56bcae6f31b7 Mon Sep 17 00:00:00 2001 From: lionsdev Date: Sat, 3 Jan 2026 15:23:47 +0000 Subject: [PATCH] fix: Ajout des classes Entity et Mapper manquantes pour Audit --- .../server/impl/entity/AuditLogEntity.java | 209 ++++++++++++++++++ .../server/impl/mapper/AuditLogMapper.java | 179 +++++++++++++++ 2 files changed, 388 insertions(+) create mode 100644 src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java create mode 100644 src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java diff --git a/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java b/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java new file mode 100644 index 0000000..b984608 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/server/impl/entity/AuditLogEntity.java @@ -0,0 +1,209 @@ +package dev.lions.user.manager.server.impl.entity; + +import dev.lions.user.manager.enums.audit.TypeActionAudit; +import io.quarkus.hibernate.orm.panache.PanacheEntity; +import jakarta.persistence.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + * Entité JPA pour la persistance des logs d'audit en base de données PostgreSQL. + * + *

Cette entité représente un enregistrement d'audit qui track toutes les actions + * effectuées sur les utilisateurs du système (création, modification, suppression, etc.).

+ * + *

Utilisation:

+ *
+ * AuditLogEntity auditLog = new AuditLogEntity();
+ * auditLog.setUserId("user-123");
+ * auditLog.setAction(TypeActionAudit.CREATION_UTILISATEUR);
+ * auditLog.setDetails("Utilisateur créé avec succès");
+ * auditLog.setAuteurAction("admin");
+ * auditLog.setTimestamp(LocalDateTime.now());
+ * auditLog.persist();
+ * 
+ * + * @see dev.lions.user.manager.server.api.dto.AuditLogDTO + * @see TypeActionAudit + * @author Lions Development Team + * @version 1.0.0 + * @since 2026-01-02 + */ +@Entity +@Table( + name = "audit_logs", + indexes = { + @Index(name = "idx_audit_user_id", columnList = "user_id"), + @Index(name = "idx_audit_action", columnList = "action"), + @Index(name = "idx_audit_timestamp", columnList = "timestamp"), + @Index(name = "idx_audit_auteur", columnList = "auteur_action") + } +) +@Data +@EqualsAndHashCode(callSuper = true) +public class AuditLogEntity extends PanacheEntity { + + /** + * ID de l'utilisateur concerné par l'action. + *

Peut être null pour les actions système qui ne concernent pas un utilisateur spécifique.

+ */ + @Column(name = "user_id", length = 255) + private String userId; + + /** + * Type d'action effectuée (CREATION_UTILISATEUR, MODIFICATION_UTILISATEUR, etc.). + *

Stocké en tant que STRING pour faciliter la lecture en base de données.

+ */ + @Column(name = "action", nullable = false, length = 100) + @Enumerated(EnumType.STRING) + private TypeActionAudit action; + + /** + * Détails complémentaires sur l'action effectuée. + *

Peut contenir des informations contextuelles comme les champs modifiés, + * les raisons d'une action, ou des messages d'erreur.

+ */ + @Column(name = "details", columnDefinition = "TEXT") + private String details; + + /** + * Identifiant de l'utilisateur qui a effectué l'action. + *

Généralement l'username ou l'ID de l'administrateur/utilisateur connecté.

+ */ + @Column(name = "auteur_action", nullable = false, length = 255) + private String auteurAction; + + /** + * Timestamp précis de l'action. + *

Utilisé pour l'ordre chronologique des logs et le filtrage temporel.

+ */ + @Column(name = "timestamp", nullable = false) + private LocalDateTime timestamp; + + /** + * Adresse IP de l'auteur de l'action. + *

Utile pour la traçabilité et la détection d'anomalies.

+ */ + @Column(name = "ip_address", length = 45) + private String ipAddress; + + /** + * User-Agent du client (navigateur, application, etc.). + *

Permet d'identifier le type de client utilisé pour l'action.

+ */ + @Column(name = "user_agent", length = 500) + private String userAgent; + + /** + * Nom du realm Keycloak concerné. + *

Important dans un environnement multi-tenant pour isoler les logs par realm.

+ */ + @Column(name = "realm_name", length = 255) + private String realmName; + + /** + * Indique si l'action a réussi ou échoué. + *

Permet de filtrer facilement les actions en erreur pour analyse.

+ */ + @Column(name = "success", nullable = false) + private Boolean success = true; + + /** + * Message d'erreur en cas d'échec de l'action. + *

Null si success = true.

+ */ + @Column(name = "error_message", columnDefinition = "TEXT") + private String errorMessage; + + /** + * Constructeur par défaut requis par JPA. + */ + public AuditLogEntity() { + this.timestamp = LocalDateTime.now(); + } + + /** + * Recherche tous les logs d'audit pour un utilisateur donné. + * + * @param userId ID de l'utilisateur + * @return Liste des logs triés par timestamp décroissant + */ + public static java.util.List findByUserId(String userId) { + return list("userId = ?1 order by timestamp desc", userId); + } + + /** + * Recherche tous les logs d'audit d'un type d'action donné. + * + * @param action Type d'action + * @return Liste des logs triés par timestamp décroissant + */ + public static java.util.List findByAction(TypeActionAudit action) { + return list("action = ?1 order by timestamp desc", action); + } + + /** + * Recherche tous les logs d'audit pour un auteur donné. + * + * @param auteurAction Identifiant de l'auteur + * @return Liste des logs triés par timestamp décroissant + */ + public static java.util.List findByAuteur(String auteurAction) { + return list("auteurAction = ?1 order by timestamp desc", auteurAction); + } + + /** + * Recherche tous les logs d'audit dans une période donnée. + * + * @param startDate Date de début (inclusive) + * @param endDate Date de fin (inclusive) + * @return Liste des logs dans la période, triés par timestamp décroissant + */ + public static java.util.List findByPeriod(LocalDateTime startDate, LocalDateTime endDate) { + return list("timestamp >= ?1 and timestamp <= ?2 order by timestamp desc", startDate, endDate); + } + + /** + * Recherche tous les logs d'audit pour un realm donné. + * + * @param realmName Nom du realm + * @return Liste des logs triés par timestamp décroissant + */ + public static java.util.List findByRealm(String realmName) { + return list("realmName = ?1 order by timestamp desc", realmName); + } + + /** + * Supprime tous les logs d'audit plus anciens qu'une date donnée. + *

Utile pour la maintenance et le respect des politiques de rétention.

+ * + * @param beforeDate Date limite (les logs avant cette date seront supprimés) + * @return Nombre de logs supprimés + */ + public static long deleteOlderThan(LocalDateTime beforeDate) { + return delete("timestamp < ?1", beforeDate); + } + + /** + * Compte le nombre d'actions effectuées par un auteur donné. + * + * @param auteurAction Identifiant de l'auteur + * @return Nombre d'actions + */ + public static long countByAuteur(String auteurAction) { + return count("auteurAction = ?1", auteurAction); + } + + /** + * Compte le nombre d'échecs pour un utilisateur donné. + *

Utile pour détecter des problèmes récurrents.

+ * + * @param userId ID de l'utilisateur + * @return Nombre d'échecs + */ + public static long countFailuresByUserId(String userId) { + return count("userId = ?1 and success = false", userId); + } +} diff --git a/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java new file mode 100644 index 0000000..2685c21 --- /dev/null +++ b/src/main/java/dev/lions/user/manager/server/impl/mapper/AuditLogMapper.java @@ -0,0 +1,179 @@ +package dev.lions.user.manager.server.impl.mapper; + +import dev.lions.user.manager.dto.audit.AuditLogDTO; +import dev.lions.user.manager.server.impl.entity.AuditLogEntity; +import org.mapstruct.*; + +import java.util.List; + +/** + * Mapper MapStruct pour la conversion entre AuditLogEntity (JPA) et AuditLogDTO (API). + * + *

Ce mapper gère la transformation bidirectionnelle entre l'entité de persistance + * et le DTO exposé via l'API REST, avec mapping automatique des champs compatibles.

+ * + *

Fonctionnalités:

+ *
    + *
  • Conversion Entity → DTO pour lecture/API
  • + *
  • Conversion DTO → Entity pour persistance
  • + *
  • Mapping de listes pour opérations bulk
  • + *
  • Gestion automatique des types LocalDateTime
  • + *
  • Mapping des enums (TypeActionAudit)
  • + *
+ * + *

Utilisation:

+ *
+ * {@literal @}Inject
+ * AuditLogMapper mapper;
+ *
+ * // Entity → DTO
+ * AuditLogDTO dto = mapper.toDTO(entity);
+ *
+ * // DTO → Entity
+ * AuditLogEntity entity = mapper.toEntity(dto);
+ *
+ * // Liste Entity → Liste DTO
+ * List<AuditLogDTO> dtos = mapper.toDTOList(entities);
+ * 
+ * + * @see AuditLogEntity + * @see AuditLogDTO + * @author Lions Development Team + * @version 1.0.0 + * @since 2026-01-02 + */ +@Mapper( + componentModel = MappingConstants.ComponentModel.JAKARTA_CDI, + injectionStrategy = InjectionStrategy.CONSTRUCTOR, + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +public interface AuditLogMapper { + + /** + * Convertit une entité AuditLogEntity en DTO AuditLogDTO. + * + *

Mapping des champs Entity → DTO:

+ *
    + *
  • id (Long) → id (String)
  • + *
  • userId → ressourceId
  • + *
  • action → typeAction
  • + *
  • details → description
  • + *
  • auteurAction → acteurUsername
  • + *
  • timestamp → dateAction
  • + *
  • ipAddress → ipAddress
  • + *
  • userAgent → userAgent
  • + *
  • realmName → realmName
  • + *
  • success → success
  • + *
  • errorMessage → errorMessage
  • + *
+ * + * @param entity L'entité JPA à convertir (peut être null) + * @return Le DTO correspondant, ou null si l'entité est null + */ + @Mapping(target = "id", source = "id", qualifiedByName = "longToString") + @Mapping(target = "ressourceId", source = "userId") + @Mapping(target = "typeAction", source = "action") + @Mapping(target = "description", source = "details") + @Mapping(target = "acteurUsername", source = "auteurAction") + @Mapping(target = "dateAction", source = "timestamp") + AuditLogDTO toDTO(AuditLogEntity entity); + + /** + * Convertit un DTO AuditLogDTO en entité AuditLogEntity. + * + *

Utilisé pour créer une nouvelle entité à persister depuis les données API.

+ * + *

Note: L'ID de l'entité sera null (auto-généré par la DB), + * même si l'ID du DTO est renseigné.

+ * + * @param dto Le DTO à convertir (peut être null) + * @return L'entité JPA correspondante, ou null si le DTO est null + */ + @Mapping(target = "id", ignore = true) // L'ID sera généré par la DB + @Mapping(target = "userId", source = "ressourceId") + @Mapping(target = "action", source = "typeAction") + @Mapping(target = "details", source = "description") + @Mapping(target = "auteurAction", source = "acteurUsername") + @Mapping(target = "timestamp", source = "dateAction") + AuditLogEntity toEntity(AuditLogDTO dto); + + /** + * Convertit une liste d'entités en liste de DTOs. + * + *

Utile pour les recherches qui retournent plusieurs résultats.

+ * + * @param entities Liste des entités à convertir (peut être null ou vide) + * @return Liste des DTOs correspondants, ou liste vide si entities est null/vide + */ + List toDTOList(List entities); + + /** + * Convertit une liste de DTOs en liste d'entités. + * + *

Utile pour les opérations d'import ou de création en masse.

+ * + * @param dtos Liste des DTOs à convertir (peut être null ou vide) + * @return Liste des entités correspondantes, ou liste vide si dtos est null/vide + */ + List toEntityList(List dtos); + + /** + * Met à jour une entité existante avec les données d'un DTO. + * + *

Préserve l'ID de l'entité et ne met à jour que les champs + * présents dans le DTO.

+ * + *

Utilisation:

+ *
+     * AuditLogEntity existingEntity = AuditLogEntity.findById(id);
+     * mapper.updateEntityFromDTO(dto, existingEntity);
+     * existingEntity.persist();
+     * 
+ * + * @param dto Le DTO source contenant les nouvelles valeurs + * @param entity L'entité cible à mettre à jour + */ + @Mapping(target = "id", ignore = true) // Préserve l'ID existant + @Mapping(target = "userId", source = "ressourceId") + @Mapping(target = "action", source = "typeAction") + @Mapping(target = "details", source = "description") + @Mapping(target = "auteurAction", source = "acteurUsername") + @Mapping(target = "timestamp", source = "dateAction") + @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) + void updateEntityFromDTO(AuditLogDTO dto, @MappingTarget AuditLogEntity entity); + + /** + * Convertit un Long (ID de l'entité) en String (ID du DTO). + * + *

MapStruct appelle automatiquement cette méthode pour le mapping de l'ID.

+ * + * @param id L'ID de type Long (peut être null) + * @return L'ID converti en String, ou null si l'input est null + */ + @Named("longToString") + default String longToString(Long id) { + return id != null ? id.toString() : null; + } + + /** + * Convertit un String (ID du DTO) en Long (ID de l'entité). + * + *

Utilisé lors de la conversion DTO → Entity si nécessaire.

+ * + * @param id L'ID de type String (peut être null) + * @return L'ID converti en Long, ou null si l'input est null ou invalide + */ + @Named("stringToLong") + default Long stringToLong(String id) { + if (id == null || id.isBlank()) { + return null; + } + try { + return Long.parseLong(id); + } catch (NumberFormatException e) { + // Log warning et retourne null en cas de format invalide + System.err.println("WARN: Invalid ID format for conversion to Long: " + id); + return null; + } + } +}