ImportExportApi
Panoramica
ImportExportApi è il client HTTP usato dalla libreria per interagire con i due microservizi backend:
node-excel-import— endpoint{baseUrl}/import/*node-excel-export— endpoint{baseUrl}/export/*
Internamente usa fetch con AbortController per supportare l'annullamento di upload ed export. Espone metodi tipizzati per upload, test, esecuzione, polling, metadata, template e storico.
Costruttore
new ImportExportApi(baseUrl: string, options?: ImportExportApiOptions)
| Parametro | Tipo | Descrizione |
|---|---|---|
baseUrl | string | URL base — gli slash finali vengono normalizzati |
options | ImportExportApiOptions | Path personalizzati per gli endpoint (opzionale) |
ImportExportApiOptions
interface ImportExportApiOptions {
/** Path base degli endpoint di import (default: '/import'). */
importPath?: string
/** Path base degli endpoint di export (default: '/export'). */
exportPath?: string
}
Il costruttore concatena baseUrl + path normalizzando le slash:
// Default
new ImportExportApi('https://api.example.com')
// → importUrl = 'https://api.example.com/import'
// → exportUrl = 'https://api.example.com/export'
// Custom path
new ImportExportApi('https://api.example.com', {
importPath: '/v2/import',
exportPath: '/v2/export',
})
// → importUrl = 'https://api.example.com/v2/import'
// → exportUrl = 'https://api.example.com/v2/export'
/api/import e /api/export. Un consumer che dipende da quel prefisso lo passa esplicitamente: new ImportExportApi(baseUrl, { importPath: '/api/import', exportPath: '/api/export' }).Implementa 3 contratti
ImportExportApi implementa simultaneamente le tre interfacce IImportClient, IExportClient e ITemplateClient. La type union ImportExportClient è il tipo accettato dalle prop api di ImportWizard / ExportDialog.
Per backend custom (mock di test, GraphQL, SDK alternativi) puoi implementare le interfacce senza ereditare dalla classe concreta. Vedi Client interfaces.
Metodi import
uploadFile(file, risorsa, opzioniCsv?, valoriFissi?)
uploadFile(
file: File,
risorsa: string,
opzioniCsv?: Partial<CsvOpzioni>,
valoriFissi?: Record<string, unknown>
): Promise<UploadResult>
Upload del file via multipart/form-data su POST /import/upload, con i campi file, risorsa, opzioniCsv (JSON, se presenti) e fixedValues (JSON di valoriFissi, solo se non vuoto: il servizio lo fonde su ogni riga; dalla 0.2.5). Crea un AbortController interno usato da cancelUpload(). Se annullato, lancia Error("Upload annullato dall'utente").
testImport(sessionId, mappings)
testImport(sessionId: string, mappings: MappingColonna[]): Promise<TestImportResult>
Dry-run su POST /import/test. Body: { sessionId, mappings }.
executeImport(sessionId, mappings)
executeImport(sessionId: string, mappings: MappingColonna[]): Promise<ImportResult>
Esecuzione effettiva su POST /import/execute. Body: { sessionId, mappings }.
getImportStatus(importId)
getImportStatus(importId: string): Promise<ImportProgresso>
Polling status di un import asincrono — GET /import/status/{importId}.
uploadImages(sessionId, files)
uploadImages(
sessionId: string,
files: File[]
): Promise<{ caricati: number; errori: string[] }>
Upload immagini collegate alla sessione di import — POST /import/images (multipart con images[]).
downloadTemplate(risorsa, formato)
downloadTemplate(risorsa: string, formato: string): Promise<Blob>
Scarica template d'esempio della risorsa — GET /import/template/{risorsa}?formato={formato}. Restituisce un Blob.
getImportHistory(risorsa?)
getImportHistory(risorsa?: string): Promise<ImportHistoryItem[]>
Storico importazioni — GET /import/history?risorsa=.... Senza risorsa restituisce tutto lo storico accessibile all'utente.
cancelUpload()
cancelUpload(): void
Annulla l'upload in corso (no-op se non c'è upload attivo).
Metodi export
executeExport(request)
executeExport(
request: ExportRequest
): Promise<ServerAsyncExportPayload | Blob>
Esecuzione export — POST /export/execute. Comportamento polimorfico:
- Risposta
2xxcon content-type binario (application/...otext/csv) →Blob - Risposta
202 Accepted→ServerAsyncExportPayload(export asincrono, partirà il polling) - Altre risposte JSON →
ServerAsyncExportPayload
Mappa ExportRequest (formato libreria) sul DTO del microservizio:
ExportRequest | Corpo della richiesta |
|---|---|
risorsa, formato, campi | stessi nomi |
nomeFile | fileName (default export_<risorsa>) |
modelloId | templateId, numerico |
filtri | filtri, intatti |
ordinamento | opzioni.ordinamento: tutti i criteri, con direzione in maiuscolo e nulls in maiuscolo solo se presente; senza criteri opzioni manca |
// ordinamento: [{ campo: 'datagiro', direzione: 'desc' }, { campo: 'orapartenza', direzione: 'desc', nulls: 'last' }]
"opzioni": {
"ordinamento": [
{ "campo": "datagiro", "direzione": "DESC" },
{ "campo": "orapartenza", "direzione": "DESC", "nulls": "LAST" }
]
}
Fino alla 0.6.0 andava solo il primo criterio; nulls va dalla 0.7.0. Ordinamento su più colonne, between e array di condizioni nei filtri richiedono node-excel-export ≥ 1.1.14, nulls la 1.1.15 (la 1.1.14 lo scarta senza errore).
Annullabile via cancelExport().
getExportStatus(exportId)
getExportStatus(exportId: string): Promise<ExportProgresso>
Polling export asincrono — GET /export/status/{exportId}.
downloadExport(url)
downloadExport(url: string): Promise<Blob>
Download del file generato. Validazione CSRF: l'origin dell'URL deve coincidere con l'origin di exportUrl, altrimenti viene rifiutato con Error("URL download non autorizzato: ...").
cancelExport()
cancelExport(): void
Annulla l'export in corso.
Metodi template
getTemplates(risorsa)
getTemplates(risorsa?: string): Promise<ModelloExport[]>
GET /export/templates?risorsa=.... Senza risorsa restituisce array vuoto. Il backend risponde con { risorsa, count, templates } o un array diretto — l'API normalizza entrambi e applica mapMsTemplateToModello per convertire dal formato microservizio (MsTemplate) al formato libreria (ModelloExport).
createTemplate(template)
createTemplate(
template: Omit<ModelloExport, 'id' | 'dataCreazione' | 'dataModifica' | 'creatoDa'>
): Promise<ModelloExport>
POST /export/templates. La libreria mappa campi: string[] → campijson: Array<{ nomecampo, etichetta, ordinamento }>: etichetta è template.etichette[nome], l'etichetta che l'utente vede, o il nome se manca. Fino alla 0.7.0 era sempre il nome tecnico ({ "nomecampo": "datagiro", "etichetta": "datagiro" }).
updateTemplate(id, updates)
updateTemplate(
id: string,
updates: Partial<ModelloExport>
): Promise<ModelloExport>
PUT /export/templates/{id}. Solo i campi presenti in updates vengono inviati al server (patch semantics). Con campi, campijson porta le etichette di updates.etichette come in createTemplate.
deleteTemplate(id)
deleteTemplate(id: string): Promise<void>
DELETE /export/templates/{id}.
Metodi metadata
getImportFieldMetadata(risorsa)
getImportFieldMetadata(risorsa: string): Promise<CampoMetadato[]>
GET /import/metadata/{risorsa}/fields. Restituisce i campi destinazione disponibili per l'import.
getExportFieldMetadata(risorsa)
getExportFieldMetadata(risorsa: string): Promise<CampoMetadato[]>
GET /export/metadata/{risorsa}/fields. Restituisce i campi esportabili.
Esempio standalone
import { ImportExportApi } from '@pzeta/vue-importexport'
const api = new ImportExportApi('https://api.example.com')
// Upload + test + execute
const upload = await api.uploadFile(file, 'anagrafica', { separatore: ';' })
const test = await api.testImport(upload.sessionId, mappings)
if (test.righeErrore === 0) {
const result = await api.executeImport(upload.sessionId, mappings)
console.log(`Importate: ${result.righeImportate}`)
}
Esempio export con polling
// downloadBlob della libreria è interno: qui un helper dell'app
function downloadBlob(blob: Blob, nomeFile: string) {
const url = URL.createObjectURL(blob)
const a = Object.assign(document.createElement('a'), { href: url, download: nomeFile })
a.click()
URL.revokeObjectURL(url)
}
const result = await api.executeExport({
risorsa: 'anagrafica',
formato: 'xlsx',
campi: ['nome', 'cognome', 'email'],
nomeFile: 'Anagrafiche 2026-10-15',
filtri: { attivo: true, datanascita: { operator: 'between', value: ['1980-01-01', '1989-12-31'] } },
ordinamento: [
{ campo: 'cognome', direzione: 'asc' },
{ campo: 'email', direzione: 'asc', nulls: 'last' },
],
})
if (result instanceof Blob) {
// Sincrono: salva direttamente
downloadBlob(result, 'anagrafica.xlsx')
} else {
// Asincrono: poll fino a completamento
let status = result
while (status.stato === 'in_corso') {
await new Promise((r) => setTimeout(r, 2000))
status = await api.getExportStatus(result.exportId)
}
if (status.urlDownload) {
const blob = await api.downloadExport(status.urlDownload)
downloadBlob(blob, status.nomeFile ?? 'export.xlsx')
}
}
Gestione errori
Dalla 0.8.0 le risposte non-2xx lanciano ApiError, esportato dal pacchetto:
class ApiError extends Error {
readonly name: 'ApiError'
/** Stato HTTP; 0 se la richiesta non è arrivata al servizio */
readonly status: number
/** `message` del corpo della risposta, se è una stringa non vuota */
readonly messaggioServizio: string | null
}
| Campo | Contenuto |
|---|---|
status | Stato HTTP della risposta. 0 quando fetch fallisce per rete (servizio non raggiungibile, CORS, DNS): l'errore originale è in cause |
messaggioServizio | Il message al primo livello del corpo: gli errori applicativi di node-excel-export lo scrivono per l'utente (es. il 403 sulla condivisione). Il 400 di validazione ha il dettaglio annidato in error e lascia null |
message | Il testo tecnico di sempre, per i log: Errore API: {status}: {message}, Errore export: …, Errore download: …, Errore eliminazione template: …. Il {message} è body.message, poi body.error, poi res.statusText |
import { ApiError } from '@pzeta/vue-importexport'
try {
await api.createTemplate(modello)
} catch (e) {
if (e instanceof ApiError && e.status === 403) {
console.warn(e.messaggioServizio) // «Solo un amministratore può condividere…»
}
}
Lo stato 0 lo danno tutte le chiamate tranne executeExport, dove un errore di rete resta l'errore di fetch. L'annullamento non diventa mai un ApiError: cancelUpload() e cancelExport() fanno lanciare Error("Upload annullato dall'utente") / Error("Export annullato dall'utente").
useApi, useCancelHandler, usePolling
useApi (pubblico) e gli helper interni per gestione abort e polling generico.
Client interfaces (DI)
Contratti IImportClient, IExportClient, ITemplateClient per sostituire il backend senza ereditare da ImportExportApi. Pattern Dependency Injection per testing, mock e SDK custom.