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).
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
ValidationPipede NestJS avecwhitelist: truesupprime automatiquement les proprietes non declarees. SiforbidNonWhitelisted: trueest 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
ValidationPipeglobal soit configure avecwhitelist: true. Si ce n'est pas le cas, le controller ou le service doit verifier l'absence des champs serveur. Le code d'erreurE-400-READONLY-FIELDpeut 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