generated from gilles/template-webapp
170 lines
5.4 KiB
Markdown
170 lines
5.4 KiB
Markdown
# Contrat d'erreurs API
|
|
|
|
Ce document définit le format standard des erreurs de l'API HomeStock.
|
|
|
|
---
|
|
|
|
## Format des réponses d'erreur
|
|
|
|
### Erreurs métier (HTTPException)
|
|
|
|
Toutes les erreurs métier utilisent `fastapi.HTTPException` et retournent :
|
|
|
|
```json
|
|
{
|
|
"detail": "Message d'erreur lisible"
|
|
}
|
|
```
|
|
|
|
<!-- complété par codex -->
|
|
|
|
### Erreurs de validation (Pydantic / FastAPI)
|
|
|
|
Les erreurs de validation des paramètres ou du corps de requête retournent automatiquement :
|
|
|
|
```json
|
|
{
|
|
"detail": [
|
|
{
|
|
"type": "validation_error",
|
|
"loc": ["body", "name"],
|
|
"msg": "String should have at least 1 character",
|
|
"input": ""
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
<!-- complété par codex -->
|
|
|
|
### Erreurs internes (500)
|
|
|
|
Le gestionnaire global (`main.py`) retourne :
|
|
|
|
```json
|
|
{
|
|
"detail": "Message technique (dev) ou 'Erreur interne du serveur' (prod)",
|
|
"type": "internal_server_error"
|
|
}
|
|
```
|
|
|
|
En développement, le message contient les détails de l'exception.
|
|
En production, le message est masqué pour la sécurité.
|
|
|
|
<!-- complété par codex -->
|
|
|
|
---
|
|
|
|
## Codes HTTP utilisés
|
|
|
|
| Code | Signification | Usage |
|
|
|------|---------------|-------|
|
|
| **200** | OK | GET, PUT, PATCH réussis |
|
|
| **201** | Created | POST réussi (création de ressource) |
|
|
| **204** | No Content | DELETE réussi (documents) |
|
|
| **400** | Bad Request | Données invalides (type de fichier, taille, auto-référence) |
|
|
| **404** | Not Found | Ressource inexistante (item, catégorie, emplacement, boutique, document, fichier physique) |
|
|
| **409** | Conflict | Conflit d'unicité (nom dupliqué, n° de série) ou dépendance empêchant la suppression |
|
|
| **422** | Unprocessable Entity | Erreur de validation Pydantic (format automatique FastAPI) |
|
|
| **500** | Internal Server Error | Exception non gérée |
|
|
|
|
<!-- complété par codex -->
|
|
|
|
---
|
|
|
|
## Codes d'erreur par domaine
|
|
|
|
### Items (`/api/v1/items`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 404 | Item non trouvé | `Objet {id} non trouvé` |
|
|
| 404 | Catégorie référencée inexistante | `Catégorie {id} non trouvée` |
|
|
| 404 | Emplacement référencé inexistant | `Emplacement {id} non trouvé` |
|
|
| 409 | N° de série déjà utilisé | `Un objet avec le numéro de série '{sn}' existe déjà` |
|
|
|
|
### Catégories (`/api/v1/categories`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 404 | Catégorie non trouvée | `Catégorie {id} non trouvée` |
|
|
| 409 | Nom déjà utilisé | `Une catégorie avec le nom '{name}' existe déjà` |
|
|
| 409 | Suppression avec items liés | `Impossible de supprimer : {n} objet(s) utilisent cette catégorie` |
|
|
|
|
### Emplacements (`/api/v1/locations`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 404 | Emplacement non trouvé | `Emplacement {id} non trouvé` |
|
|
| 404 | Parent inexistant | `Emplacement parent {id} non trouvé` |
|
|
| 400 | Auto-référence | `Un emplacement ne peut pas être son propre parent` |
|
|
| 409 | Suppression avec items liés | `Impossible de supprimer : {n} objet(s) utilisent cet emplacement` |
|
|
| 409 | Suppression avec sous-emplacements | `Impossible de supprimer : cet emplacement a {n} sous-emplacement(s)` |
|
|
|
|
### Boutiques (`/api/v1/shops`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 404 | Boutique non trouvée | `Boutique {id} non trouvée` |
|
|
| 409 | Nom déjà utilisé | `Une boutique avec le nom '{name}' existe déjà` |
|
|
| 409 | Suppression avec items liés | `Impossible de supprimer : {n} objet(s) sont associés à cette boutique` |
|
|
|
|
### Documents (`/api/v1/documents`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 404 | Item parent inexistant | `Item {id} non trouvé` |
|
|
| 404 | Document non trouvé | `Document non trouvé` |
|
|
| 404 | Fichier physique manquant | `Fichier non trouvé sur le disque` |
|
|
| 400 | Type MIME non autorisé | `Type de fichier non autorisé : {mime}. Types acceptés : images (JPEG, PNG, GIF, WebP) et PDF` |
|
|
| 400 | Photo sans image | `Le type 'photo' nécessite un fichier image` |
|
|
| 400 | Fichier trop volumineux | `Fichier trop volumineux ({size} Mo). Taille max : 10 Mo` |
|
|
|
|
### Import CSV (`/api/v1/import`)
|
|
|
|
| Code HTTP | Situation | Message |
|
|
|-----------|-----------|---------|
|
|
| 400 | Fichier non CSV | `Le fichier doit être un CSV (.csv)` |
|
|
| 400 | Indices de sélection invalides | `Format d'indices invalide` |
|
|
| 400 | Statut d'item invalide | `Statut invalide : {status}` |
|
|
|
|
<!-- complété par codex -->
|
|
|
|
---
|
|
|
|
## Conventions
|
|
|
|
### Champs obligatoires
|
|
|
|
Toute réponse d'erreur contient au minimum le champ `detail` (string ou array).
|
|
|
|
<!-- complété par codex -->
|
|
|
|
### Messages
|
|
|
|
- Les messages sont en **français**, destinés à l'utilisateur final
|
|
- Ils incluent le contexte nécessaire (ID de la ressource, nom dupliqué, taille du fichier)
|
|
- En production (500), le message technique est masqué
|
|
|
|
### Gestion côté client
|
|
|
|
Le frontend intercepte les erreurs via un intercepteur Axios (`api/client.ts`) qui log l'erreur dans la console. Le champ `detail` est affiché à l'utilisateur.
|
|
|
|
<!-- complété par codex -->
|
|
|
|
---
|
|
|
|
## Schéma de réponse (schemas/common.py)
|
|
|
|
```python
|
|
class ErrorResponse(BaseModel):
|
|
detail: str # Message d'erreur
|
|
type: str # Type d'erreur (ex: "internal_server_error")
|
|
|
|
class SuccessResponse(BaseModel):
|
|
message: str # Message de succès
|
|
id: int | None # ID de l'élément concerné
|
|
```
|
|
|
|
<!-- complété par codex -->
|