From 101e52236f9524887f6d174d2ad951e4a99fc610 Mon Sep 17 00:00:00 2001 From: SaidSoighiri94 Date: Mon, 22 Jun 2026 10:46:50 +0200 Subject: [PATCH] refactor(backend): add output DTOs for existing endpoints --- .../src/controllers/auth.controller.ts | 5 ++- .../src/controllers/emprunt.controller.ts | 3 +- .../src/controllers/materiel.controller.ts | 3 +- eme-backend/src/dtos/emprunt.dto.ts | 22 ++++++++++ eme-backend/src/dtos/materiel.dto.ts | 33 +++++++++++++++ eme-backend/src/dtos/utilisateur.dto.ts | 41 +++++++++++++++++++ review.md | 9 +++- 7 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 eme-backend/src/dtos/emprunt.dto.ts create mode 100644 eme-backend/src/dtos/materiel.dto.ts create mode 100644 eme-backend/src/dtos/utilisateur.dto.ts diff --git a/eme-backend/src/controllers/auth.controller.ts b/eme-backend/src/controllers/auth.controller.ts index 42dcbe4..7242a08 100644 --- a/eme-backend/src/controllers/auth.controller.ts +++ b/eme-backend/src/controllers/auth.controller.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import { AppError } from '../errors/app-error'; import { getProfil, identifierParCarte } from '../services/utilisateur.service'; +import { toUtilisateurDto } from '../dtos/utilisateur.dto'; export async function getMe(req: Request, res: Response): Promise { const user = req.user; @@ -9,7 +10,7 @@ export async function getMe(req: Request, res: Response): Promise { } const profil = await getProfil(user.id); - res.json({ data: profil }); + res.json({ data: toUtilisateurDto(profil) }); } export async function identifier(req: Request, res: Response): Promise { @@ -19,5 +20,5 @@ export async function identifier(req: Request, res: Response): Promise { } const profil = await identifierParCarte(qr); - res.json({ data: profil }); + res.json({ data: toUtilisateurDto(profil) }); } diff --git a/eme-backend/src/controllers/emprunt.controller.ts b/eme-backend/src/controllers/emprunt.controller.ts index 05f439e..33d4fe5 100644 --- a/eme-backend/src/controllers/emprunt.controller.ts +++ b/eme-backend/src/controllers/emprunt.controller.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import { AppError } from '../errors/app-error'; import { listerMesEmpruntsEnCours } from '../services/emprunt.service'; +import { toEmpruntDto } from '../dtos/emprunt.dto'; export async function getMesEmprunts(req: Request, res: Response): Promise { const user = req.user; @@ -9,5 +10,5 @@ export async function getMesEmprunts(req: Request, res: Response): Promise } const emprunts = await listerMesEmpruntsEnCours(user.id); - res.json({ data: emprunts }); + res.json({ data: emprunts.map(toEmpruntDto) }); } diff --git a/eme-backend/src/controllers/materiel.controller.ts b/eme-backend/src/controllers/materiel.controller.ts index ff50bd6..d8b6d3e 100644 --- a/eme-backend/src/controllers/materiel.controller.ts +++ b/eme-backend/src/controllers/materiel.controller.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express'; import { AppError } from '../errors/app-error'; import { listerCatalogue } from '../services/materiel.service'; +import { toMaterielDto } from '../dtos/materiel.dto'; function parseCategorieId(value: unknown): number | undefined { if (value === undefined) { @@ -25,5 +26,5 @@ export async function getCatalogue(req: Request, res: Response): Promise { const categorieId = parseCategorieId(req.query.categorieId); const materiels = await listerCatalogue(user.campusId, { categorieId, recherche }); - res.json({ data: materiels }); + res.json({ data: materiels.map(toMaterielDto) }); } diff --git a/eme-backend/src/dtos/emprunt.dto.ts b/eme-backend/src/dtos/emprunt.dto.ts new file mode 100644 index 0000000..139c9f2 --- /dev/null +++ b/eme-backend/src/dtos/emprunt.dto.ts @@ -0,0 +1,22 @@ +import { EmpruntAvecMateriel } from '../repositories/emprunt.repository'; +import { MaterielDto, toMaterielDto } from './materiel.dto'; + +export interface EmpruntDto { + id: number; + dateEmprunt: Date; + dateRetourPrevue: Date; + dateRetourReelle: Date | null; + statut: string; + materiel: MaterielDto; +} + +export function toEmpruntDto(emprunt: EmpruntAvecMateriel): EmpruntDto { + return { + id: emprunt.id, + dateEmprunt: emprunt.dateEmprunt, + dateRetourPrevue: emprunt.dateRetourPrevue, + dateRetourReelle: emprunt.dateRetourReelle, + statut: emprunt.statut, + materiel: toMaterielDto(emprunt.materiel), + }; +} diff --git a/eme-backend/src/dtos/materiel.dto.ts b/eme-backend/src/dtos/materiel.dto.ts new file mode 100644 index 0000000..b0b74e6 --- /dev/null +++ b/eme-backend/src/dtos/materiel.dto.ts @@ -0,0 +1,33 @@ +import { MaterielAvecCategorie } from '../repositories/materiel.repository'; + +export interface CategorieDto { + id: number; + nom: string; +} + +export interface MaterielDto { + id: number; + nom: string; + marque: string; + modele: string; + reference: string; + statut: string; + etatGeneral: string; + categorie: CategorieDto; +} + +export function toMaterielDto(materiel: MaterielAvecCategorie): MaterielDto { + return { + id: materiel.id, + nom: materiel.nom, + marque: materiel.marque, + modele: materiel.modele, + reference: materiel.reference, + statut: materiel.statut, + etatGeneral: materiel.etatGeneral, + categorie: { + id: materiel.categorie.id, + nom: materiel.categorie.nom, + }, + }; +} diff --git a/eme-backend/src/dtos/utilisateur.dto.ts b/eme-backend/src/dtos/utilisateur.dto.ts new file mode 100644 index 0000000..586df9a --- /dev/null +++ b/eme-backend/src/dtos/utilisateur.dto.ts @@ -0,0 +1,41 @@ +import { UtilisateurProfil } from '../repositories/utilisateur.repository'; + +export interface RoleDto { + code: string; + libelle: string; +} + +export interface CampusDto { + id: number; + nom: string; + ville: string; +} + +export interface UtilisateurDto { + id: number; + nom: string; + prenom: string; + email: string; + classe: string | null; + role: RoleDto; + campus: CampusDto; +} + +export function toUtilisateurDto(utilisateur: UtilisateurProfil): UtilisateurDto { + return { + id: utilisateur.id, + nom: utilisateur.nom, + prenom: utilisateur.prenom, + email: utilisateur.email, + classe: utilisateur.classe, + role: { + code: utilisateur.role.code, + libelle: utilisateur.role.libelle, + }, + campus: { + id: utilisateur.campus.id, + nom: utilisateur.campus.nom, + ville: utilisateur.campus.ville, + }, + }; +} diff --git a/review.md b/review.md index dee5578..e7c0792 100644 --- a/review.md +++ b/review.md @@ -137,6 +137,12 @@ En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y - Refactor du routage : `currentUser` appliqué par sous-routeur (au lieu d'un middleware global) pour laisser la route d'identification publique. - Vérifié au runtime : `/me` 200 (profil Marie) et 401 sans en-tête ; `/carte` QR valide 200 sans en-tête (route publique), QR inconnu 404. +### Étape 17 — DTO de sortie sur les endpoints existants +- Dossier `src/dtos/` : une interface DTO + un mapper pur `toXxxDto` par ressource (utilisateur, matériel, emprunt). +- Mapping effectué dans le controller (couche présentation) ; les services continuent de renvoyer les entités (réutilisables). +- Champs internes désormais masqués : `microsoftId`, timestamps, FK brutes, `numeroInventaire`/`numeroSerie`. +- Vérifié au runtime sur `/me`, `/materiels`, `/mes-emprunts`. + --- ## Dette technique en attente @@ -149,8 +155,7 @@ En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y 4. Tailles de colonnes `@db.NVarChar(n)` (actuellement `NVARCHAR(1000)` partout) 5. Swagger/OpenAPI (Block 1, reporté après les endpoints) 6. Auth réelle Azure AD (Block 3) — remplacera l'auth simulée par en-tête -7. DTO de sortie : les endpoints renvoient l'entité Prisma brute (`microsoftId`, timestamps exposés) — à filtrer avant la prod --- -*Dernière mise à jour : 2026-06-22 — Profil et identification par QR en place (3/6 du Block 4).* +*Dernière mise à jour : 2026-06-22 — DTO de sortie en place sur les 3 endpoints de lecture.*