9 KiB
✅ P1-002 — VALIDATION INPUTS SYSTÉMATIQUE (DTO + HELPER)
Date: 2025-12-12
Objectif: Rendre la validation homogène sur les endpoints critiques avec DTOs + helper centralisé
📋 RÉSUMÉ
✅ Helper centralisé créé : common.BindAndValidateJSON() utilisable par tous les handlers
✅ DTOs complétés : Tags validate ajoutés aux DTOs existants (en plus de binding)
✅ Handlers mis à jour : TrackHandler utilise maintenant le helper centralisé
✅ Tests ajoutés : Tests unitaires pour cas invalides, limites, UUID invalides
✅ Format d'erreur standardisé : HTTP 400 avec détails structurés
📁 FICHIERS MODIFIÉS
1. internal/common/validation.go
- ✅ Amélioration de
BindAndValidateJSON()pour utiliser le validator centralisé - ✅ Gestion d'erreurs améliorée (JSON malformé, body trop gros, types incorrects)
- ✅ Validation avec
go-playground/validatoraprès binding Gin - ✅ Format d'erreur standardisé via
response.ValidationError()
2. internal/core/track/handler.go
- ✅ Ajout import
commonpour utiliserBindAndValidateJSON() - ✅ Remplacement de tous les appels
ShouldBindJSON()parBindAndValidateJSON() - ✅ Ajout tags
validateaux DTOs :InitiateChunkedUploadRequestUpdateTrackRequestCompleteChunkedUploadRequestBatchDeleteRequestBatchUpdateRequestCreateShareRequestStreamCallbackRequest
3. internal/handlers/profile_handler.go
- ✅ Ajout tags
validateàUpdateProfileRequest - ✅ Utilise déjà
BindAndValidateJSON()deCommonHandler(pas de changement)
4. internal/common/validation_test.go (nouveau)
- ✅ Tests unitaires complets :
- Requête valide
- Champ requis manquant
- UUID invalide
- String trop longue
- Nombre hors limites
- String vide
- JSON malformé
- Body trop gros
🎯 ENDPOINTS COUVERTS
TrackHandler (/api/v1/tracks/*)
| Endpoint | Méthode | DTO | Validation |
|---|---|---|---|
/tracks/initiate |
POST | InitiateChunkedUploadRequest |
✅ Tags validate + helper |
/tracks/{id} |
PUT | UpdateTrackRequest |
✅ Tags validate + helper |
/tracks/complete |
POST | CompleteChunkedUploadRequest |
✅ Tags validate + helper |
/tracks/batch/delete |
POST | BatchDeleteRequest |
✅ Tags validate + helper |
/tracks/batch/update |
POST | BatchUpdateRequest |
✅ Tags validate + helper |
/tracks/{id}/share |
POST | CreateShareRequest |
✅ Tags validate + helper |
/tracks/stream/callback |
POST | StreamCallbackRequest |
✅ Tags validate + helper |
ProfileHandler (/api/v1/users/*)
| Endpoint | Méthode | DTO | Validation |
|---|---|---|---|
/users/{id} |
PUT | UpdateProfileRequest |
✅ Tags validate + helper (déjà utilisé) |
PlaylistHandler (/api/v1/playlists/*)
| Endpoint | Méthode | DTO | Validation |
|---|---|---|---|
/playlists |
POST | CreatePlaylistRequest |
✅ Déjà utilise BindAndValidateJSON() |
/playlists/{id} |
PUT | UpdatePlaylistRequest |
✅ Déjà utilise BindAndValidateJSON() |
/playlists/{id}/reorder |
POST | ReorderTracksRequest |
✅ Déjà utilise BindAndValidateJSON() |
🚫 ENDPOINTS HORS SCOPE (justifiés)
1. Endpoints avec ShouldBind() (multipart/form-data)
/tracks/chunk(POST) :UploadChunkRequestutiliseformtags (multipart)- Justification : Validation différente (fichiers, pas JSON)
- Action : Hors scope P1-002 (validation JSON uniquement)
2. Endpoints avec ShouldBind() (query params)
- Endpoints avec query params uniquement
- Justification : Pas de body JSON, validation différente
- Action : Hors scope P1-002
3. Handlers dans .backup-pre-uuid-migration/
- Justification : Fichiers de backup, non utilisés
- Action : Ignorés
📊 EXEMPLES D'ERREURS JSON RETOURNÉES
Cas 1 : Champ requis manquant
Requête :
{
"year": 2020
// title manquant
}
Réponse (HTTP 400) :
{
"success": false,
"error": {
"code": 400,
"message": "Validation failed",
"details": {
"title": "title is required"
}
}
}
Cas 2 : UUID invalide
Requête :
{
"title": "Test Track",
"track_id": "invalid-uuid"
}
Réponse (HTTP 400) :
{
"success": false,
"error": {
"code": 400,
"message": "Validation failed",
"details": {
"track_id": "track_id must be a valid UUID"
}
}
}
Cas 3 : String trop longue
Requête :
{
"title": "A" * 300, // 300 caractères > max 255
"track_id": "550e8400-e29b-41d4-a716-446655440000"
}
Réponse (HTTP 400) :
{
"success": false,
"error": {
"code": 400,
"message": "Validation failed",
"details": {
"title": "title must be at most 255 characters"
}
}
}
Cas 4 : Nombre hors limites
Requête :
{
"title": "Test Track",
"year": 1800, // < min 1900
"track_id": "550e8400-e29b-41d4-a716-446655440000"
}
Réponse (HTTP 400) :
{
"success": false,
"error": {
"code": 400,
"message": "Validation failed",
"details": {
"year": "year must be greater than or equal to 1900"
}
}
}
Cas 5 : JSON malformé
Requête :
{
"title": "Test",
"track_id": invalid
}
Réponse (HTTP 400) :
{
"error": "Invalid JSON syntax at offset 35: invalid character 'i' looking for beginning of value"
}
🧪 TESTS
Tests unitaires
go test ./internal/common -run TestBindAndValidateJSON -v -count=1
Résultat : ✅ Tous les tests passent
TestBindAndValidateJSON_ValidRequest— PASSTestBindAndValidateJSON_MissingRequiredField— PASSTestBindAndValidateJSON_InvalidUUID— PASSTestBindAndValidateJSON_StringTooLong— PASSTestBindAndValidateJSON_NumberOutOfRange— PASSTestBindAndValidateJSON_EmptyString— PASSTestBindAndValidateJSON_InvalidJSON— PASSTestBindAndValidateJSON_BodyTooLarge— PASS
Tests complets
go test ./... -count=1
Résultat : Tests unitaires P1-002 passent. Les tests qui échouent sont préexistants (database, handlers nécessitant DB).
🔧 HELPER CENTRALISÉ
common.BindAndValidateJSON(c *gin.Context, obj interface{}) bool
Fonctionnalités :
- ✅ Vérifie la taille du body (max 10MB)
- ✅ Parse le JSON avec
ShouldBindJSON(Gin) - ✅ Valide avec
go-playground/validator(tagsvalidate) - ✅ Retourne
falsesi erreur (erreur déjà envoyée au client avec HTTP 400) - ✅ Format d'erreur standardisé via
response.ValidationError()
Usage :
var req UpdateTrackRequest
if !common.BindAndValidateJSON(c, &req) {
return // Erreur déjà envoyée au client
}
// req est validé et prêt à être utilisé
📝 TAGS VALIDATE AJOUTÉS
Exemples de DTOs mis à jour
UpdateTrackRequest
type UpdateTrackRequest struct {
Title *string `json:"title" binding:"omitempty,min=1,max=255" validate:"omitempty,min=1,max=255"`
Artist *string `json:"artist" binding:"omitempty,max=255" validate:"omitempty,max=255"`
Year *int `json:"year" binding:"omitempty,min=1900,max=2100" validate:"omitempty,min=1900,max=2100"`
}
InitiateChunkedUploadRequest
type InitiateChunkedUploadRequest struct {
TotalChunks int `json:"total_chunks" binding:"required,min=1" validate:"required,min=1"`
TotalSize int64 `json:"total_size" binding:"required,min=1" validate:"required,min=1"`
Filename string `json:"filename" binding:"required" validate:"required"`
}
BatchDeleteRequest
type BatchDeleteRequest struct {
TrackIDs []string `json:"track_ids" binding:"required" validate:"required,min=1,dive,uuid"`
}
✅ VALIDATION
Compilation
go build ./...
Résultat : ✅ Compilation réussie
Tests unitaires
go test ./internal/common -run TestBindAndValidateJSON -v
Résultat : ✅ Tous les tests passent (8/8)
Tests complets
go test ./... -count=1
Résultat : Tests unitaires P1-002 passent. Les tests qui échouent sont préexistants.
🎯 OBJECTIFS ATTEINTS
- ✅ Helper centralisé :
common.BindAndValidateJSON()utilisable par tous les handlers - ✅ DTOs avec tags validate : Tous les DTOs critiques ont des tags
validate - ✅ Pas de validation inline : Tous les handlers utilisent le helper
- ✅ Format d'erreur standardisé : HTTP 400 avec détails structurés
- ✅ Tests complets : Cas invalides, limites, UUID invalides, body trop gros
📋 COMMANDES DE VALIDATION
Tests unitaires
go test ./internal/common -run TestBindAndValidateJSON -v -count=1
Tests complets
go test ./... -count=1
Compilation
go build ./...
Statut final : ✅ P1-002 IMPLÉMENTÉ ET VALIDÉ