Erreurs
Quand l'API refuse une requête, elle répond toujours de la même façon : un statut HTTP, une phrase en anglais pour les humains, et un code stable pour les programmes.
Pourquoi un code, et pas seulement une phrase ?
Le site et l'application affichent leurs messages en français ou en anglais. S'ils reconnaissaient les erreurs à leur phrase anglaise, une simple faute corrigée dans l'API (« has already been taken » devenu « is already taken ») casserait silencieusement leur traduction.
Le code, lui, ne change jamais : week_validated restera week_validated. Chaque client le traduit dans sa langue. C'est le même principe que les API de GitHub ou de Google, et la norme RFC 9457.
Les trois formes de réponse
Une erreur générale (400, 401, 403, 409)
{ "error": "week validated", "code": "week_validated" }Des valeurs refusées (422)
Une erreur par champ, avec ses paramètres quand le message en a besoin :
{
"errors": { "end_date": ["must be less than 31 days after the start date"] },
"details": { "end_date": [{ "code": "too_long", "max": 31 }] }
}errors: les phrases anglaises, pour les développeurs et les journaux ;details: le contrat pour les clients, uncodepar erreur et ses paramètres (count,max). Le site affiche « La date de fin doit être à moins de 31 jours du début » à partir detoo_longetmax: 31.
Une ressource introuvable (404)
{ "error": "user not found", "code": "not_found" }Pour un identifiant inconnu dans une route générique, Phoenix répond aussi avec sa forme par défaut : {"errors": {"detail": "Not Found"}}.
D'où viennent les erreurs
Plugs.Authenticaterépond lui-même 401 quand personne n'est connecté.- Tout le reste passe par
FallbackController: le{:error, …}sorti duwithd'un contrôleur y devient une réponse. - Un changeset refusé passe par
ChangesetJSON, qui produiterrorsetdetails.
Le code d'une erreur 422 vient d'Ecto quand la règle est une validation standard (validate_required donne required, une contrainte d'unicité taken…), ou de l'option code: quand la règle est écrite à la main :
add_error(changeset, :start, "overlaps another working time", code: :overlaps)Tous les codes
Erreurs générales
| Code | Statut | Quand |
|---|---|---|
bad_request | 400 | paramètres illisibles (une date mal écrite, un corps manquant) |
unauthorized | 401 | personne n'est connecté, session expirée, mauvais identifiants |
forbidden | 403 | connecté, mais pas le droit (voir Droits par rôle) |
not_found | 404 | la ressource n'existe pas |
conflict | 409 | une règle des données refuse l'action (révoquer un manager qui a encore une équipe, rouvrir une semaine déjà rouverte…) |
week_validated | 409 | la semaine est validée : ses heures, absences et demandes sont verrouillées |
already_clocked_in | 409 | « arrivée » alors que la personne est déjà arrivée |
not_clocked_in | 409 | « départ » ou pause alors que la personne n'est pas arrivée |
already_on_break | 409 | « début de pause » pendant une pause |
not_on_break | 409 | « fin de pause » sans pause en cours |
Valeurs refusées (422, dans details)
Les validations standard d'Ecto
| Code | Sens |
|---|---|
required | champ obligatoire manquant |
taken | valeur déjà utilisée (un e-mail, un nom d'équipe, une semaine déjà validée…) |
too_short, too_long | longueur hors limites (avec count ou max) |
invalid_choice | valeur hors de la liste permise (un rôle, une action inconnue…) |
invalid_format | format refusé (un e-mail sans @) |
invalid | type impossible à lire (un UUID mal formé, une date invalide) |
Les règles du métier
| Code | Champ | Quand |
|---|---|---|
in_future | end, work_date, time | une heure ou une date qui n'est pas encore arrivée |
not_after_start | end | une fin avant le début (journée, pause) |
overlaps | start, breaks | deux journées d'une personne, ou deux pauses, se chevauchent |
outside | breaks | une pause hors de sa journée |
incomplete | breaks | une pause sans début ou sans fin |
too_old | work_date, time | une correction de plus de 4 semaines, une heure envoyée par le téléphone de plus de 72 h (avec max) |
out_of_order | time | une heure du téléphone avant le pointage ou la pause précédente |
before_start | end_date | une absence qui finit avant de commencer |
no_weekday | end_date | une période d'absence sans aucun jour de semaine |
not_monday | week_start | une semaine doit commencer un lundi |
not_over | week_start | on ne valide qu'une semaine terminée |
not_active | user_id | ajouter à une équipe un compte encore invité |
not_a_manager | manager_id | nommer à la tête d'une équipe quelqu'un qui n'est pas manager |
wrong_password | current_password | le mot de passe actuel est faux |
same_as_current | email | la nouvelle adresse est déjà celle du compte |
use_email_link | email | l'e-mail ne se change pas ici : par le lien envoyé à la nouvelle adresse |
general_manager | role | le rôle du General Manager ne se change pas |
no_uppercase | password | le mot de passe n'a pas de majuscule |
no_special | password | le mot de passe n'a pas de caractère spécial |
Retrouver un code dans le code
grep -rn "code: :overlaps" timemanager-api/lib donne la règle exacte qui produit overlaps. La page Swagger (/api/docs) montre aussi, pour chaque route, les statuts qu'elle peut renvoyer.
Côté clients
| Client | Lire le code |
|---|---|
| Site web | errorCode(error) pour une erreur générale, errorDetail(error, 'champ') pour une valeur refusée (src/utils/api.js) |
| Application mobile | voir Parler à l'API |
Ajouter une règle
Toute nouvelle règle écrite à la main doit avoir son code:, ajouté à cette page. Un client ne doit jamais comparer la phrase anglaise.