Skip to content

Sources — Import fichier vers table PostgreSQL

Le module Sources permet de charger un fichier déposé (CSV, Excel, JSON) directement dans une table PostgreSQL dédiée à l'entreprise, sans écrire de SQL.

Vue d'ensemble

mermaid
sequenceDiagram
    participant Admin
    participant Frontend
    participant Backend
    participant S3
    participant PostgreSQL

    Admin->>Frontend: Sélectionne fichier + nom source + nom table
    Frontend->>Backend: POST /sources {documentUuid, name, tableName}
    Backend->>Backend: Vérifie accès document (enterprise scope)
    Backend->>S3: Télécharge le buffer du fichier
    S3-->>Backend: Buffer binaire
    Backend->>Backend: Parse CSV / Excel / JSON → headers + rows
    Backend->>Backend: Sanitise les en-têtes (unicode → ASCII, dédup)
    Backend->>PostgreSQL: CREATE SCHEMA IF NOT EXISTS schema_{uuid}
    Backend->>PostgreSQL: DROP TABLE IF EXISTS + CREATE TABLE
    Backend->>PostgreSQL: INSERT par batch de 1000 lignes (transaction)
    Backend->>Backend: Sauvegarde sources (Prisma)
    Backend-->>Frontend: SourceResponseDto {uuid, rowCount, status...}
    Frontend-->>Admin: Notification "N lignes chargées"

Schéma PostgreSQL dédié

Chaque entreprise dispose d'un schéma PostgreSQL isolé :

schema_{uuid_enterprise_sans_tirets}

Exemple : l'entreprise 550e8400-e29b-41d4-a716-446655440000 → schéma schema_550e8400e29b41d4a716446655440000.

Ce schéma est créé à la première source (CREATE SCHEMA IF NOT EXISTS). Toutes les tables de l'entreprise y sont stockées.

Flux détaillé

1. Vérification d'accès et résolution du document

Fichier : back/src/api/sources/sources.service.ts

Le service récupère le document depuis prisma.tool.documents en incluant l'entreprise du dossier. Pour un admin non super-admin, la requête est scopée à son enterprise_id. Pour un super-admin, tous les documents sont accessibles.

Si le document est introuvable ou que le dossier/entreprise est manquant → SourceDocumentNotFoundException (404).

2. Téléchargement depuis S3

Fichier : back/src/domains/s3Helper/downloadFileBuffer.ts

Le buffer binaire est téléchargé depuis Scaleway S3 via le client AWS SDK. Le chemin S3 est stocké dans documents.path.

3. Parsing du fichier

Fichier : back/src/api/sources/services/file-parser.service.ts

FormatBibliothèqueDétection
CSVPapaParseAuto-détection délimiteur (, ou ;)
Excel (.xlsx / .xls)xlsxPremière feuille uniquement
JSONnatifTableau d'objets racine

Retourne { headers: string[], rows: Record<string, string | null>[] }.

En cas d'erreur (fichier corrompu, JSON invalide, CSV sans en-tête) → SourceParseException (400) avec le message de l'erreur sous-jacente.

4. Sanitisation des en-têtes

Fichier : back/src/api/sources/services/schema-table.service.tssanitizeHeaders()

Chaque en-tête est normalisé pour être un identifiant PostgreSQL valide :

  • Suppression des accents (NFD)
  • Passage en minuscules
  • Remplacement des caractères non alphanumériques par _
  • Suppression du préfixe non-alphabétique
  • Troncature à 63 caractères
  • Déduplication : si deux colonnes donnent le même identifiant après sanitisation → col, col_2, col_3...

5. Chargement de la table

Fichier : back/src/api/sources/services/schema-table.service.tsloadTable()

sql
-- 1. Création du schéma si absent
CREATE SCHEMA IF NOT EXISTS "schema_abc123..."

-- 2. Dans une transaction :
DROP TABLE IF EXISTS "schema_abc123..."."ma_table"
CREATE TABLE "schema_abc123..."."ma_table" (
  _id SERIAL PRIMARY KEY,
  "colonne_1" TEXT,
  "colonne_2" TEXT,
  ...
)

-- 3. Insertion par batch de 1000 lignes (configurable via SOURCE_INSERT_BATCH_SIZE)
INSERT INTO "schema_abc123..."."ma_table" ("colonne_1", "colonne_2") VALUES (...)

Tous les identifiants (schéma, table, colonnes) passent par pg-format pour prévenir les injections SQL. La transaction garantit l'atomicité : en cas d'erreur à l'insertion, la table est droppée avec les données partielles.

6. Persistance Prisma

Une entrée est créée dans prisma.tool.sources avec le statut ACTIVE, le nombre de lignes, le nom du schéma, le nom de la table, et les relations vers le document et l'entreprise.

Vérification de table existante

Endpoint : GET /sources/table-check?tableName=xxx&documentUuid=yyy

Permet au frontend d'avertir l'admin avant soumission. Le backend :

  1. Résout le schéma de l'entreprise depuis documentUuid
  2. Interroge information_schema.tables :
sql
SELECT EXISTS (
  SELECT 1 FROM information_schema.tables
  WHERE table_schema = 'schema_...' AND table_name = 'ma_table'
)

Retourne { exists: boolean }.

Le frontend déclenche cet appel avec un debounce de 400 ms quand le champ tableName de l'étape 2 du wizard change. Si exists = true, un banner orange non bloquant est affiché.

Fichiers concernés

FichierRôle
back/src/api/sources/sources.controller.tsEndpoints REST + Swagger
back/src/api/sources/sources.service.tsOrchestration métier
back/src/api/sources/services/file-parser.service.tsParsing CSV/Excel/JSON
back/src/api/sources/services/schema-table.service.tsGestion schémas et tables PostgreSQL
back/src/api/sources/dto/source.dto.tsDTO entrants et sortants
back/src/api/sources/source.presenter.tsProjection Prisma → DTO
back/src/exceptions/source.exceptions.tsExceptions custom (6 types)
back/prisma/schema.prismaModèle sources
front/src/pages/admin/sources/AdminSourcesPage.vuePage admin liste des sources
front/src/components/sources/SourceCreationDialog.vueWizard de création (3 étapes)
front/src/stores/source-store.jsStore Pinia sources
front/src/router/sourcesGuard.jsGuard route /sources (admin uniquement)

Variables d'environnement

VariableRôleDéfaut
SOURCE_INSERT_BATCH_SIZENombre de lignes par batch d'insertion1000
DATABASE_URL_TOOLConnexion PostgreSQL données métier (Prisma)
UPLOAD_S3_BUCKETBucket S3 Scaleway
UPLOAD_S3_ENDPOINTEndpoint S3 Scaleway
UPLOAD_S3_REGIONRégion S3
UPLOAD_S3_ACCESS_KEYClé d'accès S3
UPLOAD_S3_SECRET_ACCESS_KEYSecret S3

Décisions d'architecture liées

  • ADR-01 : PostgreSQL indépendant pour les données métier — les tables sources y sont créées dans des schémas dédiés par entreprise.
  • ADR-02 : Scaleway Object Storage (S3) pour les fichiers — le module Sources télécharge depuis S3 avant parsing.
  • ADR-03 : Upload via NestJS — le buffer transite par le backend, jamais directement côté client.