Sanitize les attributs en LECTURE aussi, pas seulement a l'ecriture
Build and deploy / test-python (push) Successful in 11m5s
Build and deploy / test-js (push) Successful in 55s
Build and deploy / lint-python (push) Successful in 6m29s
Build and deploy / lint-js (push) Failing after 1m39s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 5m18s

Bug reel constate le 21/09/2026 : un element Scenario cree avant la
refonte en arbre de decision (ancien schema plat "situation"/"choices"/
"correct_index") faisait planter silencieusement le panneau Proprietes
cote client des la selection - scenario.nodes etait inexistant sur
l'ancienne forme, aucune erreur visible, juste "il ne se passe rien".
Cause : document_edit.py et document_render.py renvoyaient les
attributs BRUTS de la base au client, jamais revalides - contrairement
a la route d'ecriture qui, elle, sanitize deja avant de persister.

Ajoute document_engine.sanitize_element_attributes(kind, attributes),
point d'entree unique de dispatch kind -> sanitize_X_config, utilise
desormais a la fois en ecriture (document_element_update.py, qui
reutilise ce nouveau dispatch au lieu de son if/elif duplique) ET en
lecture (document_edit.py/document_render.py). Elimine toute la classe
de bug "schema devenu obsolete apres une evolution du modele de
donnees d'un mini-jeu, donnee jamais retouchee depuis" - present et
futur, pas seulement pour Scenario.

Migre les donnees reelles deja affectees (support de test, element 46)
vers le nouveau schema en arbre, en preservant integralement le
contenu deja redige par l'utilisateur (situation + 3 choix/consequences
du scenario "chat sur la route").

Ajoute un test de non-regression qui ecrit delibirement l'ancien schema
en base (en contournant le sanitize de la route d'ecriture, pour
simuler une donnee reellement ancienne jamais nettoyee) puis verifie
que /edit et /render renvoient une structure saine au client.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-21 08:07:06 +02:00
co-authored by Claude Sonnet 5
parent a529857379
commit 7e504b7865
7 changed files with 134 additions and 23 deletions
+2
View File
@@ -63,6 +63,7 @@ from .labels.quiz_config import (
quiz_total_points,
sanitize_quiz_config,
)
from .labels.sanitize_element_attributes import sanitize_element_attributes
from .labels.scenario_config import (
DEFAULT_SCENARIO_CONFIG,
MAX_SCENARIO_CHOICES,
@@ -103,6 +104,7 @@ __all__ = [
"render_document",
"render_document_element",
"sanitize_association_config",
"sanitize_element_attributes",
"sanitize_memory_config",
"sanitize_mots_config",
"sanitize_quiz_config",
+20
View File
@@ -240,3 +240,23 @@ philosophie que le reste de ce module : ne jamais faire échouer une
structure entière pour une seule référence cassée.
- **Retour** : dict complet (mêmes clés que `DEFAULT_SCENARIO_CONFIG`).
- **Exceptions** : aucune.
## `sanitize_element_attributes.py` — point d'entrée unique de revalidation par kind
Dispatch `kind -> sanitize_X_config` (`_SANITIZERS`, privée) pour les 5
mini-jeux à structure garantie (Quiz/Association/Memory/Mots
mêlés/Scénario) — les autres kinds n'ont pas de sanitizer et sont
renvoyés tels quels. Utilisé à la fois à l'ÉCRITURE
(`routes/document/document_element_update.py`) ET à la LECTURE
(`routes/document/document_edit.py`, `routes/document/document_render.py`)
: sans ce second usage, des attributs stockés dans un schéma devenu
obsolète (ex. Scénario, passé d'une liste plate à un arbre de décision)
atteindraient le panneau Propriétés côté client TELS QUELS, qui suppose
la forme ACTUELLE — bug réel constaté le 21/09/2026, un élément Scénario
créé avant la refonte en arbre faisait planter silencieusement le
panneau Propriétés (`scenario.nodes` inexistant sur l'ancienne forme).
### `sanitize_element_attributes(kind: str, attributes: Any) -> Any`
- **Retour** : `attributes` revalidé si `kind` a un sanitizer, sinon
`attributes` tel quel.
- **Exceptions** : aucune (délègue à des sanitizers qui ne lèvent jamais).
@@ -0,0 +1,37 @@
"""Point d'entrée UNIQUE de revalidation des attributs d'un élément par
kind — utilisé à la fois à l'ÉCRITURE (routes/document/
document_element_update.py) ET à la LECTURE (routes/document/
document_edit.py, routes/document/document_render.py). Sans ce second
usage en lecture, des attributs stockés dans un schéma devenu obsolète
(ex. le mini-jeu Scénario, passé d'une liste plate à un arbre de
décision) atteindraient le panneau Propriétés côté client TELS QUELS,
qui suppose la forme ACTUELLE — bug réel constaté le 21/09/2026 : un
élément Scénario créé avant la refonte en arbre faisait planter
silencieusement le panneau Propriétés (`scenario.nodes` inexistant sur
l'ancienne forme), sans qu'aucun message n'indique pourquoi."""
from typing import Any
from .association_config import sanitize_association_config
from .memory_config import sanitize_memory_config
from .mots_config import sanitize_mots_config
from .quiz_config import sanitize_quiz_config
from .scenario_config import sanitize_scenario_config
_SANITIZERS = {
"quiz": sanitize_quiz_config,
"association": sanitize_association_config,
"memory": sanitize_memory_config,
"mots": sanitize_mots_config,
"scenario": sanitize_scenario_config,
}
def sanitize_element_attributes(kind: str, attributes: Any) -> Any:
"""Revalide `attributes` selon `kind`, UNIQUEMENT pour les kinds à
structure garantie (les 5 mini-jeux ci-dessus, voir `_SANITIZERS`) —
les autres kinds (texte/forme/image/bouton/rangée) sont de simples
valeurs scalaires sans structure à garantir, renvoyés TELS QUELS
(jamais sanitizés : ce ne sont pas leur responsabilité ici)."""
sanitizer = _SANITIZERS.get(kind)
return sanitizer(attributes) if sanitizer else attributes