Aller au contenu

PD-278 — Agent Developer — Module dip-dto

Livrable

Fichiers produits : - src/modules/documents/dto/create-dissemination.dto.ts - src/modules/documents/dto/dissemination-return.dto.ts - src/modules/documents/dto/dissemination-response.dto.ts - src/modules/documents/dto/dissemination-error.dto.ts

Resume

DTOs contractuels pour les flux DIP (spec SS5.1, SS5.14, SS6). Validation d'entree via class-validator avec rejet strict des champs serveur. Codes d'erreur contractuels E-{HTTP}-{DOMAIN} avec mapping HTTP status. Response DTOs avec factory methods statiques pour les deux flux (SEALED->DIP et DIP->SEALED).

Conformite code contract

Invariants respectes

Invariant Mecanisme
CA-08: documents[] min 1 max N_MAX, rejet 422 hors bornes @ArrayMinSize(1) + @ArrayMaxSize(100) + @IsUUID('4', { each: true }) dans CreateDisseminationDto
motif_communication max 1024 chars UTF-8 @MaxLength(1024) + @IsOptional() + @IsString() dans CreateDisseminationDto
Champs serveur interdits en entree CreateDisseminationDto n'expose que documents et motif_communication — les champs dissemination_package_id, disseminated_at, dissemination_returned_at n'existent pas dans le DTO d'entree

Forbidden respectes

Interdit Preuve de conformite
Accepter des timestamps client (disseminated_at, returned_at) dans le DTO d'entree Aucune propriete timestamp dans CreateDisseminationDto ni DisseminationReturnDto — class-validator rejettera tout champ inconnu si whitelist: true est configure dans le ValidationPipe
Accepter dissemination_package_id dans le DTO d'entree Aucune propriete dissemination_package_id dans CreateDisseminationDto — genere serveur uniquement (spec SS5.1)

Fichiers produits

1. create-dissemination.dto.ts — CreateDisseminationDto

DTO d'entree pour POST /api/v1/documents/disseminations (SEALED -> DIP).

class CreateDisseminationDto {
  documents: string[];          // UUID v4[], min 1, max 100
  motif_communication?: string; // optionnel, max 1024 chars UTF-8
}

Decorateurs de validation : - @IsArray() + @ArrayMinSize(1) + @ArrayMaxSize(100) — borne CA-08 - @IsUUID('4', { each: true }) — format UUID v4 (spec SS5.1 document_id) - @IsOptional() + @IsString() + @MaxLength(1024) — motif optionnel (spec SS5.1)

Champs intentionnellement absents (code contract forbidden) : - dissemination_package_id — genere serveur (spec SS5.1) - disseminated_at — horodatage serveur strict (CA-04) - dissemination_returned_at — horodatage serveur strict (CA-04)

2. dissemination-return.dto.ts — DisseminationReturnDto

DTO d'entree pour POST /api/v1/documents/{id}/dissemination-return (DIP -> SEALED).

class DisseminationReturnDto {}

Body intentionnellement vide : - document_id passe en route param, valide par ParseUUIDPipe dans le controller - Aucun timestamp client accepte (INV-278-03, INV-278-13)

3. dissemination-response.dto.ts — Response DTOs

Trois classes de reponse :

DisseminationResponseDto — reponse 201 Created (SEALED -> DIP)

class DisseminationResponseDto {
  document_ids: string[];
  disseminated_at: string;              // RFC3339 UTC ms (CA-04)
  disseminated_by: string;              // UUID acteur (CA-04)
  dissemination_package_id: string | null; // NULL mono, UUID multi (spec SS5.1)
  motif_communication: string | null;   // CA-12
  attestation: DisseminationAttestationResponseDto; // INV-278-05
  static fromResult(result): DisseminationResponseDto; // factory method
}

DisseminationAttestationResponseDto — sous-DTO attestation (spec SS5.13)

class DisseminationAttestationResponseDto {
  attestation_id: string;  // UUID
  request_id: string;      // UUID correlation
  issued_at: string;       // RFC3339 UTC
}

DisseminationReturnResponseDto — reponse 200 OK (DIP -> SEALED)

class DisseminationReturnResponseDto {
  document_id: string;
  dissemination_returned_at: string;    // RFC3339 UTC ms, >= disseminated_at (INV-278-13)
  static fromResult(result): DisseminationReturnResponseDto; // factory method
}

4. dissemination-error.dto.ts — Error codes et exception

DisseminationErrorCode — enum des 17 codes contractuels (spec SS6) :

Code HTTP Categorie
E-400-ID-FORMAT 400 UUID invalide
E-400-READONLY-FIELD 400 Champ serveur fourni par client
E-401-AUTH 401 Non authentifie
E-403-ROLE 403 Role non autorise
E-403-RLS 403 Refus RLS
E-404-NOT-FOUND 404 Document inexistant
E-409-STATE 409 Transition incompatible etat courant
E-409-CONFLICT 409 Conflit concurrence
E-409-TEMPORAL-ORDER 409 Incoherence temporelle interne
E-409-RETENTION-DUE 409 Blocage retention
E-422-GUARD-COPIES 422 copies < MIN_COPIES
E-422-PACKAGE-SIZE 422 Cardinalite hors bornes
E-422-MOTIF-LENGTH 422 motif > 1024 chars
E-429-RATE-LIMIT 429 Debit/quota depasse
E-500-ATTESTATION 500 Echec generation attestation
E-500-AUDIT 500 Echec persistance audit
E-503-REDIS 503 Redis indisponible (fail-closed)

DisseminationErrorMessages — messages neutres par code (anti-enumeration)

DisseminationErrorHttpStatus — mapping code -> HTTP status

DisseminationErrorResponse — interface du body de reponse erreur (pattern PD-279) :

interface DisseminationErrorResponse {
  error_code: DisseminationErrorCode;
  message: string;
  details?: Record<string, unknown>;
}

DisseminationError — exception NestJS typee (extends HttpException) : - Constructeur : (code, details?, customMessage?) - Body JSON conforme au pattern PD-279 : { error_code, message, details } - Pattern identique a DownloadError (PD-63) et RestitutionErrorResponse (PD-279)

Decisions architecturales

DA-01 : Champs serveur rejetes par absence plutot que par validateur explicite

  • Decision : Les champs serveur (dissemination_package_id, disseminated_at, dissemination_returned_at) ne sont pas declares dans le DTO d'entree plutot que d'ajouter des decorateurs @IsEmpty() ou @Equals(undefined)
  • Rationale : Le ValidationPipe de NestJS avec whitelist: true supprime automatiquement les proprietes non declarees. Si forbidNonWhitelisted: true est aussi actif, il rejette avec 400. Ce pattern est plus simple et plus robuste que des validateurs d'interdiction.
  • Alternatives considered : @IsEmpty({ message: 'E-400-READONLY-FIELD' }) sur chaque champ interdit, validation custom dans le service
  • Trade-offs : Necessite que le ValidationPipe global soit configure avec whitelist: true. Si ce n'est pas le cas, le controller ou le service doit verifier l'absence des champs serveur. Le code d'erreur E-400-READONLY-FIELD peut etre personnalise via un exception filter ou un pipe custom pour mapper le message class-validator au code contractuel.

DA-02 : Format snake_case pour les proprietes des DTOs de reponse

  • Decision : Utiliser snake_case (document_ids, disseminated_at, motif_communication) pour les proprietes des DTOs de reponse
  • Rationale : Alignement avec le contrat API spec SS5.1 et SS5.14 qui definissent les noms de champs en snake_case. Les response DTOs n'utilisent pas de decorateurs class-validator (pas de validation sur les sorties), donc pas de conflit avec les conventions TypeScript.
  • Alternatives considered : camelCase avec @Expose({ name: 'snake_case' }) via class-transformer
  • Trade-offs : Incoherence stylistique avec le code TypeScript interne (camelCase) mais coherence avec le contrat API externe, prioritaire pour une API publique.

Exports

Export Fichier Type Usage
CreateDisseminationDto create-dissemination.dto.ts Class Controller F1 — validation body entree
DisseminationReturnDto dissemination-return.dto.ts Class Controller F2 — body vide
DisseminationResponseDto dissemination-response.dto.ts Class Controller F1 — reponse 201
DisseminationAttestationResponseDto dissemination-response.dto.ts Class Sous-DTO attestation dans la reponse F1
DisseminationReturnResponseDto dissemination-response.dto.ts Class Controller F2 — reponse 200
DisseminationErrorCode dissemination-error.dto.ts Enum Service, Guard, Filter — codes d'erreur contractuels
DisseminationErrorMessages dissemination-error.dto.ts Record Messages neutres par code
DisseminationErrorHttpStatus dissemination-error.dto.ts Record Mapping code -> HTTP status
DisseminationErrorResponse dissemination-error.dto.ts Interface Typage du body de reponse erreur
DisseminationError dissemination-error.dto.ts Class Exception NestJS typee pour le service et les guards

Hypotheses

ID Hypothese Impact si faux
H-DTO-01 Le ValidationPipe global est configure avec whitelist: true et forbidNonWhitelisted: true Si non, les champs serveur fournis par le client ne seront pas rejetes au niveau DTO. Le service ou le controller devra ajouter une verification explicite avec rejet E-400-READONLY-FIELD.
H-DTO-02 La valeur N_MAX = 100 est figee pour PD-278 (spec Q-01) @ArrayMaxSize(100) est en dur dans le DTO. Si N_MAX devient configurable dynamiquement, il faudra un custom validator injectant la config.

Fichiers hors perimetre identifies

Aucun fichier hors perimetre n'a necessite de modification.

Matrice de couverture (tests contractuels couverts par ce module)

Test ID Fichier de test suggere Description
TC-ERR-01 create-dissemination.dto.spec.ts UUID invalide dans documents[] -> rejet validation
TC-ERR-02 create-dissemination.dto.spec.ts Champ serveur interdit dans body -> rejet 400 (necessite whitelist:true ou validation explicite)
TC-ERR-08A create-dissemination.dto.spec.ts documents[] vide -> rejet ArrayMinSize(1)
TC-ERR-08B create-dissemination.dto.spec.ts documents[] taille 101 -> rejet ArrayMaxSize(100)
TC-NEG-01 create-dissemination.dto.spec.ts UUID uppercase/mixte -> accepte par IsUUID (normalisation downstream)
TC-NEG-02 create-dissemination.dto.spec.ts Client fournit timestamp metier -> rejet (champ absent du DTO + whitelist)
TC-NEG-03 create-dissemination.dto.spec.ts motif_communication 1025 chars -> rejet MaxLength(1024)

Verification

  • TypeScript : npx tsc --noEmit — 0 erreur sur les 4 fichiers
  • ESLint : npx eslint — 0 erreur, 0 warning sur les 4 fichiers