Skip to content

POC — Liste des démarches de l'organisation - #128

Draft
damienlethiec wants to merge 8 commits into
mainfrom
feat/568-liste-demarches-organisation
Draft

POC — Liste des démarches de l'organisation#128
damienlethiec wants to merge 8 commits into
mainfrom
feat/568-liste-demarches-organisation

Conversation

@damienlethiec

Copy link
Copy Markdown
Collaborator

⚠️ POC — à ne pas merger en l'état

Cette branche est une preuve de concept, produite en une session de développement assistée.
Elle est très imparfaite et uniquement éprouvée en local contre la pile de développement
interne. Elle n'a tourné sur aucun environnement partagé.

Je n'ai pas relu ce code. Le comportement a été vérifié en navigateur et par la suite de
tests ; le code lui-même n'a reçu aucune relecture humaine. À lire comme une proposition à
challenger ligne à ligne, pas comme un travail prêt à être validé.

Elle est publiée pour être discutée et reprise, pas pour être intégrée telle quelle.

Ticket de suivi interne : 568 (« un agent n'a aucune vue listant les démarches de son
organisation »).


Ce que ça fait

Un agent connecté arrive sur la liste paginée des démarches de sa structure, et peut en ouvrir
le détail.

  • GET /demarches — tableau DSFR : numéro, flux, état, dates. / y redirige l'agent connecté.
  • GET /demarches/:id — le détail, réduit aux métadonnées, demandeur compris.
  • Autorisation par Pundit : DeliveryPolicy::Scope borne la liste aux flux habilités,
    DeliveryPolicy#show? borne le détail. Le sujet des policies est le rattachement, pas
    l'agent : rôle et habilitations vivent sur lui.
  • Un membre sans habilitation obtient un écran explicite, sans aucun appel réseau.
  • Aucune erreur de l'API amont ne produit de page d'erreur : la page se rend avec un message
    qui distingue indisponibilité, périmètre ambigu et structure introuvable.

C'est la première policy du dépôt — Pundit entre dans le bundle avec cette branche.

Le point qui mérite une relecture attentive

DeliveryPolicy#show? ferme un trou qui n'était pas dans l'énoncé du ticket : l'API amont ne
borne que sur l'organisation
, pas sur les habilitations de l'agent. Sans cette vérification, un
membre habilité sur un seul flux pouvait ouvrir le détail d'une démarche d'un autre flux de sa
structure en connaissant son identifiant — la liste ne la lui montrait pas, l'URL directe si.

Refus et inexistence renvoient volontairement le même message : les distinguer révélerait
l'existence de démarches hors périmètre.

Écarts assumés par rapport au ticket

Deux critères d'acceptation ne sont pas satisfaisables en l'état, et ce sont des contradictions
entre l'énoncé et l'API livrée — pas des choix de conception :

  1. La liste exige un état. L'API refuse une liste sans filtre de statut, jusque dans son SQL.
    Or le ticket exclut les onglets et les filtres. La vue ouvre donc sur un état par défaut,
    changeable par paramètre d'URL, sans interface — le ticket dédié aux onglets n'aura qu'à se
    poser par-dessus.
  2. Le demandeur n'est pas dans la projection de liste, délibérément allégée côté API. Le
    satisfaire imposerait un appel de détail par ligne, ce qui contredit le critère de
    volumétrie. La colonne s'ajoutera le jour où l'API l'expose. Il est en revanche présent sur
    la page de détail.

Dépendance

Cette branche consomme une version non publiée de la gem cliente privée, sur sa propre branche
de POC. Le Gemfile déclare encore le tag précédent, qui ne contient pas la surcouche : la
branche ne construit donc que sur un poste où la gem locale est branchée (voir ci-dessous).
À repointer sur une source stable avant tout merge.

Comment l'essayer en local

Prérequis : la pile de développement interne démarrée (base, fournisseur de jetons, API), et un
compte de test du fournisseur d'identité.

1. Variables d'environnement — ajouter dans .env (les clés sont dans .env.example, les
valeurs viennent de la configuration de la pile de développement) :

HUB_API_BASE_URL=
KEYCLOAK_BASE_URL=
KEYCLOAK_REALM=
HUB_API_CLIENT_ID=
HUB_API_CLIENT_SECRET=
HUB_API_SCOPE=
HUB_API_SCOPE_REFERENTIAL=

⚠️ Deux scopes, et c'est nécessaire. L'API amont n'ouvre pas le référentiel et les
téléservices aux mêmes, et un jeton n'en porte qu'un seul exploitable — afficher la liste en
traverse deux. Le client déclaré auprès du fournisseur de jetons doit porter les deux, sans quoi
le symptôme est un 500 opaque et non un refus lisible.

2. Brancher la gem locale

export HUB_API_V1_PATH=../hub-api-v1
bundle install

3. Semer et lancer

bin/rails db:seed
bin/rails server

4. Se connecter. Les seeds créent un agent rattaché à l'organisation que la pile de
développement alimente réellement en démarches, et habilité sur son unique flux. Une identité
libre du fournisseur de test permet de saisir le SIRET correspondant à la connexion — c'est le
seul triplet où identité, seeds et données désignent la même organisation.

Sans cet alignement, l'écran est vide sans que rien ne l'explique : c'est le mode d'échec le
plus coûteux à diagnostiquer.

5. Vérifier — au-delà de l'affichage :

  • retirer l'habilitation de l'agent, puis recharger l'URL d'un détail : le portail doit refuser,
    alors que l'API le servirait volontiers ;
  • arrêter l'API amont, puis recharger la liste : la page doit se rendre avec une alerte, et le
    journal nommer l'exception — jamais une page d'erreur.

État de la vérification

  • bin/ci verte : 392 exemples, 0 échec, Cucumber compris
  • ✅ Parcours complet éprouvé en navigateur contre l'API réelle, en local
  • ❌ Aucun environnement partagé
  • Aucune relecture humaine du code
  • ❌ Pagination non éprouvée sur un volume réel — le jeu de données local tient sur une page

Première policy du dépôt ; Pundit passe donc dans le bundle. Le sujet est le
rattachement et non l'agent : rôle et habilitations vivent sur lui. Scope
borne la liste, show? borne le détail — sans ce second, un identifiant connu
ouvrirait une démarche hors habilitation, l'API amont ne bornant que sur
l'organisation. Le lock embarque aussi brakeman 8.0.6, qu'exige --ensure-latest.
Traduit un rattachement et un périmètre déjà autorisé en appel de liste :
SIRET en périmètre, codes de flux en filtre, page en décalage. Un périmètre
autorisé vide court-circuite l'appel — le transmettre tel quel aurait levé le
filtre au lieu de le fermer.
Les specs de la policy et du query object décrivaient leurs exemples en
français, là où tout le reste du dépôt les décrit en anglais et commente en
français. Corrigé avant que la convention ne se dédouble.
Le passer inconditionnellement obligeait à résoudre HubApiV1.client dans
l'expression d'argument, donc avant l'appel — y compris sous bouchon, où
aucune variable d'environnement n'est posée. Absent, c'est la gem qui résout
le sien, à l'intérieur de list.
Un agent connecté arrive sur ses démarches, paginées et bornées par
policy_scope. L'état ouvre sur « transmise » et se change par paramètre
d'URL : le mécanisme est posé, l'interface d'onglets viendra avec son lot.
Aucune erreur de l'API amont ne produit de page d'erreur. La racine
redirigeant désormais, les specs de session et Cucumber traversent la liste —
un bouchon par défaut la neutralise là où elle n'est pas le sujet.
Métadonnées seules — pièces jointes et historique relèvent de leurs propres
lots. Le demandeur y figure, absent de la liste servie en amont mais présent
au détail. Une démarche hors habilitation et une démarche inexistante donnent
le même message : les distinguer révélerait celles des autres périmètres.
Le logger de la gem rend visibles les erreurs de lecture de dates, jusqu'ici
silencieuses ; son require est explicite, faute de quoi il ne serait jamais
posé. Les seeds gagnent un agent rattaché à l'organisation que le socle de
développement alimente réellement, habilité sur son unique flux — sans lui,
l'écran reste vide sans que rien ne l'explique.
L'API n'ouvre pas le référentiel et les téléservices aux mêmes scopes, et un
jeton n'en porte qu'un : afficher la liste en traverse donc deux. Ces noms
sont définis par le fournisseur de jetons de notre déploiement — ils vivent
ici, la gem se contentant de transmettre ce qu'on lui passe.
@damienlethiec damienlethiec self-assigned this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant