node-excel-import
node-excel-import
In sintesi
Microservizio di importazione massiva di dati tabellari (Excel xlsx/xls e CSV) verso il database del tenant ditta. Espone una pipeline asincrona — upload → parsing → validazione → trasformazione → arricchimento → persistenza — con tracking dello stato del job (pending → processing → completed/failed) e supporto a lookup table riusabili per mapping codici. Nel grafo dei servizi è la porta d'ingresso "ETL leggero" per popolare risorse PostgreSQL a partire da fogli di calcolo prodotti dagli operatori o da sistemi esterni.
Funzionalità principali
- Upload e parsing di file
xlsx,xlsecsvviamultipart/form-data, con selezione del foglio, salto righe header, limite righe e validazione schema tramite Zod - Preview senza commit: ispezione dei primi N record (max 100) e dei nomi dei fogli, utile al frontend per costruire il mapping colonne prima dell'import effettivo
- Column mapping dichiarativo da colonna sorgente a campo target, con flag
requiredper i campi obbligatori - Trasformazioni dati componibili in catena (
uppercase,lowercase,trim,parse_number,parse_date,format_date,format_currency,round,regex_replace,default_value,required,conditional,lookup,formula) applicate per ordine configurabile - Lookup table persistite su PostgreSQL con CRUD completo, ricerca exact match e fuzzy match (distanza di Levenshtein con estensione
pg_trgm) - Risoluzione External ID: una colonna del foglio può fungere da chiave naturale; il servizio costruisce in batch il mapping External ID → primary key per decidere automaticamente fra
INSERTeUPDATE(upsert) - Data enrichment opzionale verso provider HTTP esterni configurabili, con throttling a batch per non saturare i servizi remoti
- Job tracking completo: contatori
processedRows/totalRows/rowsCreated/rowsUpdated/rowsFailed, errori e timestamp, interrogabili per id, per stato o come elenco dei più recenti - Auto-migration PostgreSQL idempotente al bootstrap, con
pg_advisory_lockper gestire più repliche sullo stesso schema
Architettura
Stack: Fastify v5 · Inversify (DI con constructor injection e decoratori @injectable()/@inject()) · @fastify/multipart per gli upload · exceljs per parsing Excel · zod per la validazione di request e righe importate · date-fns per le trasformazioni temporali · pg (driver nativo) verso PostgreSQL · @pzeta/fastify-utils (loggingPlugin, errorHandlerPlugin, healthcheckPlugin, metricsPlugin, openapiPlugin, securityPlugin con rate limit) · @pzeta/log per logging strutturato e correlation ID.
Layout DDD (src/):
| Layer | Contenuto |
|---|---|
Domain/ | Entità (ImportJob, LookupTable, ValidationResult), value object (ImportJobId, FileName, ColumnMapping, TransformationRule), interfacce repository |
Application/ | ImportService (facade), ImportOrchestrator (pipeline), ImportJobService (lifecycle stati), FileSystemService, LookupTableService, DTO e port (IExcelParser, IDataTransformer, IDataEnrichmentService) |
Infrastructure/ | Adapter ExcelParser (exceljs + Zod), DataTransformer (catena di trasformazioni), DataEnrichmentService (HTTP), repository PostgreSQL (PostgresImportJobRepository, PostgresLookupTableRepository, PostgresExternalIdRepository, GenericResourceRepository, PostgresMetadataRepository), PostgresConnection, AutoMigration, DIContainer |
Presentation/ | ImportController (upload, preview, jobs, stats) e LookupController (CRUD tabelle e record), RouteRegistration |
Pattern adottati: Facade (ImportService su ImportOrchestrator + ImportJobService), Repository con port-and-adapter sul layer dominio, Chain of Responsibility per le trasformazioni, elaborazione asincrona del job in background con aggiornamento incrementale dello stato, schema importexport autocreato e versionato tramite file 001–005 in migrations/.
Autenticazione: il servizio si affida a nginx (auth_request → node-user-auth) e non implementa identity in proprio. Il rate limit pubblico (createRateLimitConfig.forPublicApi, finestra di 15 minuti) escludelusivamente /health e /ready. CORS è guidato da CORS_ORIGINS (whitelist).
Casi d'uso
- Caricamento anagrafiche batch: l'operatore carica un
.xlsxdi clienti/fornitori; il frontend chiamaPOST /import/previewper mostrare le prime righe e configurare il mapping colonne, poiPOST /import/uploadconcolumnMappingseenableTransformation: trueper normalizzare maiuscole, codice fiscale e date - Aggiornamento massivo via External ID: il foglio contiene una colonna
codiceesternoche mappa a record già esistenti — l'orchestrator costruisce in batch la corrispondenza External ID → ID interno e produce un mixINSERT/UPDATEinvece di duplicare le righe - Normalizzazione tramite lookup: una colonna "tipo pagamento" testuale viene risolta contro una lookup table (
POST /lookup/tables/{name}/lookup/{key}) per ottenere il codice interno; eventuali valori non noti vengono individuati via fuzzy match e segnalati nei warning del job - Arricchimento da provider esterno: per ogni riga il
DataEnrichmentServicechiama un endpoint HTTP configurato (es. validazione partita IVA, geocoding indirizzi) a batch, con pausa configurabile tra i batch per rispettare i rate limit del provider - Monitoraggio import lunghi: il frontend polla
GET /import/jobs/{jobId}per mostrare la progress bar (processedRows/totalRows) e gli aggregati finali (rowsCreated/rowsUpdated/rowsFailed) a fine job
Identità & esposizione
| Campo | Valore |
|---|---|
| Categoria | data-io |
| Versione cluster | 1.0.6 |
| Image | gitea.pzetatouch.it/pzeta_touch/node-excel-import:1.0.6 |
| URL pubblico | https://ditta.pzeta.it/excel-import |
| Path regex ingress | `/excel-import(/ |
| Rewrite a backend | /$2 |
| DNS interno | node-excel-import-ditta.ditta.svc.cluster.local:3000 |
| Auth nginx | auth_request → node-user-auth |
| Repository | node-excel-import |
| Endpoint REST | 13 (vedi sezione "API reference") |
Endpoint operazionali
Endpoint convenzionali esposti da tutti i microservizi PZeta basati su @pzeta/fastify-utils:
| Path pubblico | Scopo |
|---|---|
https://ditta.pzeta.it/excel-import/health | liveness probe |
https://ditta.pzeta.it/excel-import/ready | readiness probe |
https://ditta.pzeta.it/excel-import/metrics | metriche Prometheus |
https://ditta.pzeta.it/excel-import/api-docs.json | spec OpenAPI runtime (richiede OPENAPI_EXPOSE_IN_PRODUCTION=true) |
https://ditta.pzeta.it/excel-import/api-docs | Swagger UI (solo in NODE_ENV !== production) |
Configurazione
Variabili d'ambiente rilevanti per chi integra il servizio (per la lista completa vedi .env.default del repo):
| Variabile | Ruolo |
|---|---|
HOST / PORT | Bind del server (0.0.0.0:3000 in container, 127.0.0.1:3000 di default) |
NODE_ENV | production, development o test; influisce su esposizione Swagger UI e formattazione log |
DATABASE_HOST / _PORT / _NAME / _USER / _PASSWORD | Connessione PostgreSQL — job, lookup table, risorse importate, metadati, External ID |
DATABASE_POOL_MAX | Dimensione massima del pool pg (default 20) |
AUTO_MIGRATION_ENABLED | Se false salta l'auto-migration al bootstrap; di default abilitato |
ENABLE_CORS / CORS_ORIGINS | Attiva CORS e definisce la whitelist degli origin consentiti (separati da virgola) |
REVERSE_PROXY | Se valorizzato, allunga keepAliveTimeout/headersTimeout per la convivenza con nginx |
OPENAPI_EXPOSE_IN_PRODUCTION | Espone /api-docs.json anche in produzione; Swagger UI resta off in produzione |
ENRICHMENT_BATCH_PAUSE_MS | Pausa fra batch di enrichment HTTP (default 100ms, minimo 0) |
APP_URL | URL pubblico dell'applicazione, usato in alcuni link applicativi |
Lo schema PostgreSQL importexport viene creato e versionato automaticamente al bootstrap dai file in migrations/ (sequenza 001–005); l'esecuzione è protetta da pg_advisory_lock per consentire l'avvio simultaneo di più repliche.
Note eventing NATS
Il servizio è attualmente HTTP-only: non pubblica né consuma subject NATS, non si registra come modulo presso node-orchestrator e non integra direttamente node-postgrest-sidecar. La persistenza delle righe importate avviene tramite un repository PostgreSQL generico interno che scrive nello schema target via driver pg. L'integrazione con orchestrator/eventing è prevista come evoluzione (vedi issue upstream) ma non è ancora implementata, quindi nats.json risulta correttamente vuoto.
Dipendenze e dipendenti
Dipende da (servizi che questo servizio chiama):
Consumato da (chi chiama questo servizio):
frontend Vue
Infrastruttura (PostgreSQL, NATS, Redis, MinIO) non è elencata qui — vedi sezione Architettura del singolo servizio.