Skip to content

Feature/dp 1721 spike integrer maquettes espace fd listing formulaires cas - #1595

Draft
jbfeldis wants to merge 6 commits into
developfrom
feature/dp-1721-spike-integrer-maquettes-espace-fd-listing-formulaires-cas
Draft

Feature/dp 1721 spike integrer maquettes espace fd listing formulaires cas#1595
jbfeldis wants to merge 6 commits into
developfrom
feature/dp-1721-spike-integrer-maquettes-espace-fd-listing-formulaires-cas

Conversation

@jbfeldis

@jbfeldis jbfeldis commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

No description provided.

jbfeldis added 5 commits June 3, 2026 15:13
…itationType

Étape 1.1 — migration `form_templates` (slug unique, FK habilitation_type,
service_provider_id string, jsonb steps/static_blocks/scopes_config/initialize_with),
modèle avec friendly_id, paper_trail, invariants (slug pas en collision avec
les UID YAML, au moins un default par HT, cascade-safe via destroyed_by_association).

Étape 1.2 — callback `HabilitationType#after_create :ensure_default_form_template!`
qui reprend `ordered_steps` + `form_introduction` (parité avec l'ancien
`build_form_from_habilitation_type`). Rake idempotente `form_templates:backfill_defaults`
pour les HTs existants en DB.

Façade `AuthorizationRequestForm.db_records` non touchée à ce stade (Étape 1.3
suivra) : on prépare la donnée sans changer le surface API.
…emplate

Étape 1.3 — la façade `AuthorizationRequestForm` lit désormais ses db_records
depuis `FormTemplate.includes(habilitation_type: :data_provider)`. Un HT avec
N FormTemplate produit N ARFs (1 default + N-1 non-default), au lieu d'1 ARF
unique forcé `default: true` comme dans `build_form_from_habilitation_type`
(supprimée).

Surface API publique inchangée — le `uid` côté façade reste le slug,
`authorization_request_class` est résolu via `template.habilitation_type`.
Les nouveaux champs portés en DB depuis YAML (`use_case`, `initialize_with`,
`static_blocks`, `service_provider_id`, `name`, `description`, `public`,
`startable_by_applicant`, `single_page_view`, `scopes_config`) sont
deep_symbolize_keys côté façade, conforme à ce que consomment les vues.

Specs `authorization_request_form_spec.rb` réécrites pour passer par un
vrai `FormTemplate` (au lieu de stub `db_records`), couvrant : 1 HT → N ARFs,
default flag respecté, service_provider résolu via le YAML backend, jsonb
symbolisé. Full RSpec 2631/0, full cucumber : flakes connus uniquement.

Fix i18n: l'ajout du namespace `form_template` à `activerecord.fr.yml` (commit
parent) était mal indenté et avait sectionné le bloc `habilitation_type` au
milieu de `errors.models`, faisant disparaître `scopes.scope_value_duplicate`
de la résolution i18n et cassant la validation visuelle des scopes en admin.

Docs `docs/technique/habilitation_type_dynamique.md` mises à jour pour
refléter le pont HabilitationType → FormTemplate → AuthorizationRequestForm,
retrait des limitations levées (`use_case`, `initialize_with`, `static_blocks`,
`service_provider` côté DB).
Étape 1.4 — 2 specs garantissent que `AuthorizationRequestForm.all` reflète
une création / suppression de `FormTemplate` sans appel manuel à `reset!`.
La synchronisation passe par `FormTemplate#after_save/destroy :reset_arf_cache`
qui bump le compteur Redis via `StaticApplicationRecord#reset!` (invalide
cross-process / cross-worktree, Redis partagé en dev).

Étape 1.5 — `rubocop -A` final : `Rails/WhereExists` corrigé dans la rake
de backfill (`where(default: true).exists?` → `exists?(default: true)`).
@linear

linear Bot commented Jun 3, 2026

Copy link
Copy Markdown

DP-1721

@jbfeldis
jbfeldis changed the base branch from develop to feature/dp-1718-permettre-n-templates-de-cas-dusage-pour-1-formulaire June 3, 2026 13:18
@JeSuisUnCaillou

Copy link
Copy Markdown
Collaborator

Je recopie ici ton message pour avoir les liens pratiques dans la PR


Et hop, vous pouvez jouer avec l'explo Eva Spaeter [Beta] et Valentin Shamsnejad [Beta] :
pour se connecter comme admin facilement : https://sandbox.datapass.api.gouv.fr/local-sign-in?email=datapass@yopmail.com

Puis par exemple :
Tous les FDs https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees
La DINUM https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees/dinum/formulaires/api_particulier
Un cas d'usage API-Part https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees/dinum/formulaires/api_particulier/cas-usage/api-particulier-familea

@jbfeldis
jbfeldis force-pushed the feature/dp-1721-spike-integrer-maquettes-espace-fd-listing-formulaires-cas branch from 6535092 to d39b056 Compare June 3, 2026 14:45
…on maquettes

Spike de catalogage en lecture du référentiel Formulaires + Cas d'usage,
servi sous `/instruction/fournisseurs-donnees/...` à l'audience reporter+
(admin / reporter / instructor / manager / developer via RoleHierarchy).
Stack au-dessus de PR #1564 (DP-1718 : cascade HT→FT + FormTemplate)
dont il consomme les nouveautés (`form_template`, `inherited?`,
`from_database?`).

== Routes & Pundit ==

Nouvelles routes dans `namespace :instruction` :
  - `get '/fournisseurs-donnees' → data_providers#index` (catalogue racine)
  - `scope '/fournisseurs-donnees/:provider_slug'` avec :
    - `resources :formulaires, only: %i[index show]` (AuthorizationDefinition)
    - `resources :cas_usages, only: :show, path: 'cas-usage', param: :uid`
      (AuthorizationRequestForm sous le formulaire)

Hiérarchie controllers :
  - `Instruction::DataProvidersController` < `InstructionController`
    (gate `reporter?` héritée — index seul, pas de @data_provider)
  - `Instruction::AbstractCatalogueController` < `InstructionController`
    (porte le `set_data_provider` partagé par les vues FD-scopées)
  - `Instruction::{Formulaires,CasUsages}Controller` <
    `Instruction::AbstractCatalogueController`

Trois policies namespacées `Instruction::*` :
  - DataProviderPolicy + Scope : accessible si admin OU si l'utilisateur
    a un rôle reporter+ sur au moins une définition du provider. TODO
    explicite : itération sur `AuthorizationDefinition.all` à revisiter
    une fois la migration BDD (DP-1719/1671) en place.
  - AuthorizationDefinitionPolicy + Scope : `user.reporter?(record.id)`
  - AuthorizationRequestFormPolicy : déléguée à la définition parente

Wiring via `policy_scope([:instruction, …])` sur l'index et
`authorize [:instruction, …]` dans Abstract + show controllers. Refus →
redirect vers dashboard via `Pundit::NotAuthorizedError` (comportement
existant d'`AccessAuthorization`).

== Vue Formulaire (`instruction/formulaires/show.html.erb`) ==

  - Section Identification : uid
  - Section Identité : table kind, link, access_link, cgu_link,
    support_email (URL → lien fr-link target=_blank, fallback « Non
    renseigné »)
  - Section Propriétés : table booléens public / startable_by_applicant
    / unique
  - Section Fonctionnalités : liste \`features\` si non vide
  - Section Périmètres de données : tags scopes (\`scope.name.presence
    || scope.value\`)
  - Section Structure du formulaire : blocs
  - Section Cas d'usage : cards par cas d'usage avec badges « par défaut »
    + compteurs demandes/habilitations (filtrés par form_uid sur les
    tables AuthorizationRequest et Authorization)

Listage parent \`instruction/formulaires/index\` simplifié (cards par
AuthorizationDefinition).

== Vue Cas d'usage (`instruction/cas_usages/show.html.erb`) ==

  - Header : nom + badge « Par défaut » + badge « Hérité du formulaire »
    si name est null sur le FormTemplate
  - Section Description (onglets Rendu / HTML) :
      Rendu via \`sanitize\` allowlist \`strong em b i u br a p ul li\`
      HTML via \`<pre class=\"dp-code-block\">\` source échappée
  - Section Introduction (onglets Rendu / HTML) :
      allowlist étendue à \`div\` (les introductions utilisent souvent
      \`<p>\`, \`<ul>\`, \`<li>\`, \`<div>\`, \`<a>\`)
  - Section Propriétés : table public / startable_by_applicant
    + ligne dédiée « Template single-page » (avec tooltip d'info,
    rendu nil → \"(template par défaut)\" en grisé, string →
    \`<code>nom_template</code>\`)
  - Section Parcours et blocs statiques (onglets Vue / Colonnes) :
      Vue : liste unifiée des blocs dans l'ordre catalogue parent
      (\`@formulaire.blocks\`), numérotés. Blocs présents dans
      \`static_blocks\` rendus avec fr-tag--brown-cafe-creme + cadenas
      (lock-line), même sémantique que les scopes désactivés. Blocs
      « hors catalogue » (steps/static non déclarés sur la définition
      parente) appendus avec badge warning. Single-page : badge inline
      « Page unique » + nom du template (\`single_page_view.presence ||
      uid.underscore\`).
      Colonnes : table dressant \`formulaire.blocks\`, \`steps\`,
      \`static_blocks\`, \`single_page_view\` brutes.
  - Section Configuration des périmètres (onglets Rendu / Code) :
      Rendu : tags groupés par \`scope.group\` avec icône d'état
      + check vert si scope dans \`initialize_with[:scopes]\`. Légende
      des 4 états en tête.
      Code : YAML du \`scopes_config\` + \`preselected_scopes\` propres
      au cas d'usage, puis dump \`formulaire_scopes\` avec \`name\`,
      \`value\`, \`group\`, \`link\`, \`included\`, \`disabled\`,
      \`deprecated\` (accès via \`scope.deprecated?\` /
      \`scope.deprecated_date\`).
  - Section Données initiales (onglets Rendu / Données) :
      Rendu : aperçu form-style avec \`<input>\` / \`<textarea>\` DSFR
      en \`readonly\`. Heuristique de typage par valeur (URL → type=url,
      multi-ligne ou >80 chars → textarea auto-dimensionné,
      Boolean → Oui/Non, Numeric → type=number, autre → JSON fallback).
      Cas spécial \`:modalities\` → tags DSFR.
      Données : \`<pre class=\"dp-code-block\">\` YAML brut.
  - Section Service utilisateur : nom + badge éditeur si applicable

Partial \`_rendered_html_tabs.html.erb\` : locale \`source\` (et non
\`raw\` qui shadowait le helper Rails).

== Extensions DSFR (\`dsfr-extensions.css\`) ==

  - \`.fr-tag.fr-tag--brown-cafe-creme\` : étend l'accent DSFR aux
    \`<span>\`/\`<p>\` informationnels via vars
    \`--text-action-high-brown-cafe-creme\` /
    \`--background-action-low-brown-cafe-creme\`
  - \`.dp-code-block\` : reprend le style scopé existant
    \`.markdown-content pre\` en classe générique
  - \`.dp-flow-steps\` : ol flex pour la liste numérotée du parcours

== Index providers ==

\`instruction/data_providers/index.html.erb\` : cards DSFR avec logo,
nom, slug, nombre de formulaires accessibles (filtre
\`current_user.reporter?(ad.id)\`). Header partial (\`_header.html.erb\`)
mis à jour : le titre « Espace fournisseur de données » devient un lien
retour vers l'index providers.

== Sécurité ==

Trois LinkToHref \`weak\` ajoutés à \`config/brakeman.ignore\` :
\`link_to(model.link, model.link, …)\` sur \`DataProvider.link\` et
\`AuthorizationDefinition.{link,cgu_link}\`. Les modèles valident l'URL
via \`URL_REGEX\` au write et l'espace est scopé Pundit (admin/FD only).

== Tests ==

Specs Pundit pour les trois policies (\`spec/policies/instruction/\`).

Request specs (\`spec/requests/instruction/\`) couvrent les trois
routes — pour chaque endpoint :
  - admin : happy path + erreurs 404 (mismatch provider, id inconnu)
  - FD reporter scopé sur le provider : 200
  - FD reporter scopé sur un autre provider : redirect (autorisé par
    InstructionController, refusé par la policy)
  - utilisateur sans rôle reporter : redirect dashboard
    (InstructionController gate)
  - non authentifié : redirect

== Locales ==

Fichier \`config/locales/instruction_catalogue.fr.yml\` regroupe
l'ensemble des wordings sous \`instruction.{data_providers,formulaires,
cas_usages}.*\` + titres de pages sous \`page_titles.instruction.*\`.
@jbfeldis
jbfeldis force-pushed the feature/dp-1721-spike-integrer-maquettes-espace-fd-listing-formulaires-cas branch from d39b056 to 618c085 Compare June 4, 2026 08:39
@jbfeldis

jbfeldis commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

Je recopie ici ton message pour avoir les liens pratiques dans la PR

Et hop, vous pouvez jouer avec l'explo Eva Spaeter [Beta] et Valentin Shamsnejad [Beta] : pour se connecter comme admin facilement : https://sandbox.datapass.api.gouv.fr/local-sign-in?email=datapass@yopmail.com

Puis par exemple : Tous les FDs https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees La DINUM https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees/dinum/formulaires/api_particulier Un cas d'usage API-Part https://sandbox.datapass.api.gouv.fr/espace-fournisseur-donnees/dinum/formulaires/api_particulier/cas-usage/api-particulier-familea

Pour info j'ai changé les urls pour les mettre sous /instruction donc ça donne maintenant:
/instruction/fournisseurs-donnees/dinum/formulaires/api_particulier/cas-usage/api-particulier-familea

@jbfeldis
jbfeldis force-pushed the feature/dp-1718-permettre-n-templates-de-cas-dusage-pour-1-formulaire branch 4 times, most recently from 631503e to c9486b8 Compare June 15, 2026 12:29
Base automatically changed from feature/dp-1718-permettre-n-templates-de-cas-dusage-pour-1-formulaire to develop June 15, 2026 12:41
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.

2 participants