Skip to content

BDD-168 — Entreprises manquantes dans le sélecteur super-admin

🔧 Fix technique

Entreprises manquantes dans le sélecteur super-admin

GET /enterprise-api-keys/enterprises ne renvoyait que les entreprises ayant déjà une fiche locale sur l'environnement consulté. Sur un environnement peu utilisé (ex: staging), le sélecteur d'entreprise du super-admin n'affichait quasiment que sa propre entreprise. Le endpoint provisionne maintenant la fiche locale à la volée au lieu de masquer l'entreprise.

1. Contexte

Le switcher d'entreprise du super-admin (MainLayout.vue, header de la sidebar) appelle GET /enterprise-api-keys/enterprises, implémenté dans EnterpriseApiKeysService.listEnterprises().

Ce endpoint :

  1. Récupère la liste brute des entreprises via ConsoleAdminService.getEnterprises() (API console admin, { id, name }, sans uuid).
  2. Fait correspondre chaque entreprise à sa fiche locale prisma.tool.enterprises par id, pour récupérer l'uuid local attendu par le reste de l'application (adminScopeStore, résolution du dossier racine, etc.).
  3. Filtrait toute entreprise sans correspondance locale (uuid: null).

Or une ligne tool.enterprises n'est créée que lorsqu'un utilisateur de cette entreprise se connecte à Outil BDD sur cet environnement précis (authGuard.upsertLocalUser). Sur un environnement neuf ou peu utilisé, la table locale ne contient donc que les entreprises dont un membre s'est déjà connecté — la plupart des entreprises existant côté console-admin étaient filtrées silencieusement, sans erreur.

Symptôme observé : sur staging.api.bdd.heriade.fr, un super-admin connecté ne voyait dans le sélecteur qu'une seule entreprise (la sienne), alors que console-admin en référence bien davantage. Un même code déployé sur un environnement plus ancien (avec plus de logins historiques) ne présentait pas le problème — d'où la confusion initiale sur une possible différence de configuration entre les deux environnements.

2. Solution

listEnterprises() ne filtre plus les entreprises sans uuid local : il provisionne la fiche locale à la volée (ensureLocalUuid), avec le même upsert que celui déjà utilisé à la connexion (authGuard.upsertLocalUser), keyed sur l'id numérique console-admin.

ts
async listEnterprises(): Promise<Array<{ uuid: string; name: string | null }>> {
  const consoleEnterprises = await this.consoleAdmin.getEnterprises();
  const enterprises = await Promise.all(
    consoleEnterprises.map((e) => this.ensureLocalUuid(e)),
  );
  return enterprises.sort((a, b) =>
    (a.name ?? '').localeCompare(b.name ?? '', 'fr', { sensitivity: 'base' }),
  );
}

private async ensureLocalUuid(enterprise: { id: number; name: string | null; uuid?: string | null }) {
  if (enterprise.uuid) return { uuid: enterprise.uuid, name: enterprise.name ?? null };

  const row = await this.prisma.tool.enterprises.upsert({
    where: { id: enterprise.id },
    create: { id: enterprise.id, name: enterprise.name ?? null },
    update: { name: enterprise.name ?? null },
    select: { uuid: true },
  });
  return { uuid: row.uuid, name: enterprise.name ?? null };
}

3. Périmètre technique

Backend

FichierChangement
back/src/api/enterprise-api-keys/enterprise-api-keys.service.tslistEnterprises() provisionne au lieu de filtrer ; nouvelle méthode privée ensureLocalUuid()
back/src/api/enterprise-api-keys/enterprise-api-keys.service.spec.tsTests mis à jour : provisioning à la volée, tri simple, liste vide

Aucun changement front — enterprises-store.js et MainLayout.vue consomment déjà le tableau { uuid, name }[] tel quel.

4. Règles métier implémentées

  • La clé de correspondance reste l'id numérique console-admin, jamais l'uuid. L'upsert est keyé sur where: { id } : la première "rencontre" d'une entreprise (que ce soit via ce endpoint ou via un login classique) génère l'uuid local (@default(uuid()) en base) ; toutes les rencontres suivantes ne mettent à jour que le name. Pas de risque de doublon ni de divergence d'uuid, même en cas de login concurrent sur une entreprise jamais vue sur cet environnement.
  • Aucune entreprise console-admin n'est plus masquée du sélecteur super-admin, indépendamment de l'historique de connexions sur l'environnement consulté.

5. Points de vérification

  • GET /enterprise-api-keys/enterprises en tant que super-admin sur un environnement avec peu/pas de logins locaux retourne bien toutes les entreprises console-admin, pas seulement celle de l'utilisateur connecté.
  • Après ce premier appel, tool.enterprises contient une ligne par entreprise console-admin retournée, avec un uuid stable.
  • Un login ultérieur d'un utilisateur d'une de ces entreprises ne crée pas de doublon et ne change pas l'uuid déjà provisionné.

6. Tests associés

Unitaires back

  • enterprise-api-keys.service.spec.tsdescribe('listEnterprises') :
    • tri par nom des entreprises ayant déjà un uuid local (pas d'appel upsert) ;
    • provisioning à la volée d'une entreprise sans uuid (assertion sur les paramètres exacts de l'upsert), sans qu'elle soit masquée du résultat ;
    • tableau vide si console-admin ne renvoie aucune entreprise.

7. Notes d'implémentation

  • Origine : signalé par Pierre Espieux sur staging.bdd.heriade.fr (déploiement dédié, même code que dev.bdd.heriade.fr) — le sélecteur n'affichait que l'entreprise Heriade. Diagnostic confirmé en inspectant la réponse réseau ([{ uuid, name: "Heriade" }] uniquement) et le code de listEnterprises().
  • Hors scope (volontairement) : le filtrage par environnement console-admin (Cloud / MVP / Déclaration EI…) introduit par BDD-113 sur un endpoint distinct (FolderService.listEnterprises(), GET /folders/enterprises) n'est plus câblé — ce endpoint n'existe plus dans folder.service.ts actuel, et ConsoleAdminService.getEnterprisesByEnvironment() / CONSOLE_ADMIN_ENVIRONMENT_SLUG sont aujourd'hui du code mort (testés mais jamais appelés en production). Si un filtrage par environnement redevient nécessaire pour /enterprise-api-keys/enterprises, ce sera l'objet d'un ticket dédié plutôt que d'être mélangé à ce correctif.

8. Variables d'environnement

Aucune nouvelle variable.