Skip to content

Pipeline: test-posta ​

  • Scopo: provare i connettori IMAP e SMTP di un'installazione, da soli, prima di dare la colpa alla pipeline vera
  • Ambito: core — non appartiene a un cliente: si usa su qualunque installazione che legga o mandi posta · Owner: Simone
  • Cadenza: nessuna. Si lancia a mano, quando serve
  • Tetto: 5 messaggi per run
  • Runbook: nessuno. La sezione Come si lancia esaurisce la procedura: un comando, nessun ripristino da orchestrare, nessun effetto da annullare

Non fa nulla di utile al cliente, ed è il punto. Una pipeline di posta vera usa spesso IMAP e SMTP insieme a un modello, al gestionale e all'upload su Noeva: quando non parte, i sospetti sono troppi. Questa pipeline ne isola due. Se la mail di riepilogo arriva, host, porte, TLS e credenziali di posta sono a posto e il problema è altrove.

Flusso dati ​

0/3
casella-prova — source imap host: imap.esempio porta: 993 cartella: INBOX maxMessages: 5 casella-provasourceimapcartella: INBOX salva-eml — action local-folder cartella: /percorso/di/esempio salva-emlactionlocal-foldercartella: /percorso/di/esempio riepilogo — transform riepilogotransform notifica — sink smtp host: smtp.esempio porta: 587 mittente: noeva@esempio notificasinksmtphost: smtp.esempio
Lo scopo dei nodi
NodoTipoScopo
casella-provasourceLegge alcune mail dalla cartella di posta configurata, senza segnarle come lette né spostarle. Se questo passo riesce, l'accesso alla casella funziona.
salva-emlactionSalva una copia di ogni mail letta nella cartella locale dell'installazione, per controllare che cosa è arrivato. Rilanciando la prova, le stesse mail riscrivono gli stessi file.
riepilogotransformRiunisce le mail salvate in un unico riepilogo, con quante sono e quanto pesano. Serve perché la prova mandi una mail sola invece di una per messaggio.
notificasinkManda l'unica mail di riepilogo della prova. Se arriva, l'invio della posta da questa installazione funziona.

Lo scopo di ogni nodo è nella tabella sotto il grafo, presa dal piano. Qui il dettaglio tecnico che lo scopo non dice:

NodoTipoDettaglio tecnico
postasourceIMAP, cartella configurata, al più MAX_MESSAGES mail
salva-emlactionFile <uid a 8 cifre>-<oggetto ripulito>.eml, vedi Idempotenza
riepilogotransformgroupBy su chiave costante: N messaggi → un record
notificasinkSMTP

La chiave del groupBy di riepilogo è costante proprio perché il gruppo dev'essere uno solo.

Che cosa non fa ​

Non tocca la casella. Niente \Seen, niente keyword, niente spostamenti: dopo la run la cartella è esattamente com'era. È ciò che la rende rilanciabile quante volte serve mentre si sistema una configurazione — al contrario di una pipeline che usa la cartella come coda, dove lo spostamento in Done è il commit e una mail si lavora una volta sola.

Non salva gli allegati. Sono dentro l'.eml, che è il messaggio RFC 822 integro; il riepilogo ne dichiara il numero per messaggio. Se serve verificarne i byte su disco, si apre l'.eml.

Non elenca più di 50 messaggi nella mail. Oltre quella soglia il corpo tronca e lo dichiara («… e altri N messaggi»). Col tetto di default — 5 — non succede mai; succede se alzi TEST_POSTA_MAX_MESSAGES.

Non ha dead-letter, e non è una dimenticanza. Un errore ferma la run e arriva intero nel report. In una pipeline di produzione mettere da parte il record rotto è giusto; qui il record rotto è il risultato dell'esperimento, e archiviarlo farebbe passare per riuscita una prova che non lo è. Un messaggio che IMAP non riesce a scaricare (eml nullo) alza ExtractError e nessuna mail parziale parte.

Idempotenza ​

Il nome del file è <uid a 8 cifre>-<oggetto ripulito>.eml. Due run sulla stessa casella riscrivono gli stessi file, non ne accumulano copie. Lo zero padding serve alla lettura a occhio: senza, 10.eml verrebbe prima di 9.eml.

L'oggetto che entra nel nome perde tutto ciò che non è lettera, cifra o trattino basso — punti e separatori di percorso compresi. Un oggetto costruito apposta (../../etc/passwd) non può quindi far uscire il file dalla cartella di destinazione.

Casella vuota ​

Nessun messaggio letto significa nessuna mail inviata: il groupBy non ha gruppi da emettere. La run resta un successo, e che la casella fosse vuota si legge dal report (extracted: 0), non dall'assenza della mail. Vale la pena saperlo prima di concludere che SMTP non funziona.

È il limite di questa prova, e va conosciuto: su casella vuota dimostra metà della tesi — che IMAP si collega e non trova nulla — e di SMTP non dice niente. Per provare anche l'invio serve almeno un messaggio nella cartella.

Configurazione ​

Tutte le variabili stanno in .env, nessun indirizzo e nessun percorso è nel codice. I valori fittizi sono in .env.example.

VariabileObbl.DefaultChe cos'è
TEST_POSTA_CARTELLA_LOCALEsì—Dove finiscono gli .eml
TEST_POSTA_DESTINATARIOsì—L'unico destinatario del riepilogo
TEST_POSTA_IMAP_INSTANCEnodefaultIstanza IMAP nel registry
TEST_POSTA_SMTP_INSTANCEnodefaultIstanza SMTP nel registry
TEST_POSTA_CARTELLAnoINBOXCartella IMAP da leggere
TEST_POSTA_MAX_MESSAGESno5Tetto ai messaggi per run

Host, porte e credenziali vengono dai registry dei connettori (IMAP_*, SMTP_*). Usa le stesse istanze della pipeline vera: provare istanze diverse da quelle che userà la pipeline di produzione non prova niente di ciò che interessa.

Su Gmail, Workspace e Microsoft 365 la password via IMAP e SMTP non esiste più: l'autenticazione passa da OAuth2, e questa pipeline è il modo di verificarla. Vedi il runbook oauth-google-posta.md.

Il mittente non è configurabile qui: è SMTP_FROM dell'istanza, perché un'identità diversa è un'altra istanza.

Con variabili mancanti la run si ferma prima di toccare la casella, con un ConfigError che le elenca tutte insieme, della pipeline e dei connettori: si corregge il .env in un giro solo. Le stesse si vedono prima di lanciarla con pnpm run etl env check: la pipeline dichiara le variabili che legge (environment), compresi l'utente della casella e, se la casella è su OAuth2 senza token, l'identità OAUTH_* che lo rinnova.

Che cosa mostra il DAG ​

Il pannello di ogni nodo dice, per ogni valore, da quale variabile arriva e la sua origine: env (impostata), default (presa dal codice), mancante (obbligatoria e assente), non impostata (facoltativa). Host e porta sono righe distinte, e la credenziale è una riga a parte che dice solo la variante in uso: password, access token o identità OAuth2. Con OAuth2 dice anche se l'identità OAUTH_* che rinnova il token è completa, o quali sue variabili mancano; un IMAP_USER mancante, che non ha una riga sua, lo segnala questa stessa riga.

Il piano ricade sull'esempio un'istanza alla volta. Se manca TEST_POSTA_CARTELLA_LOCALE, solo salva-eml mostra /percorso/di/esempio, con la variabile marcata mancante; la casella IMAP e il server SMTP mostrano i valori veri del .env. Allo stesso modo, un'istanza IMAP rotta non trascina le altre: se TEST_POSTA_IMAP_INSTANCE indica una casella sana, il nodo la mostra anche quando un'altra casella del registry è incompleta. Se una casella non si costruisce per un valore non valido (password e token impostati insieme, una porta non numerica), il suo nodo porta la riga configurazione: non valida, con i nomi delle variabili da controllare.

A nodo non selezionato, il piano porta la configurazione della pipeline: tutte le variabili TEST_POSTA_*, con valore e origine. È l'unico posto in cui compare TEST_POSTA_DESTINATARIO, che non è il dettaglio di nessun nodo. Una variabile mancante non ha valore: qui non c'è un segnaposto d'esempio.

Il server dei grafi calcola il piano all'avvio: dopo una modifica al .env va riavviato. L'ora di calcolo del piano dice se il riavvio è avvenuto (runbook del server).

test-posta è una pipeline pronta: un repo cliente la usa dichiarandola nel catalogo del suo etl.config.ts. Se l'installazione limita le pipeline per workspace con ETL_PIPELINE_WORKSPACES, va assegnata anche ai workspace in cui deve girare, altrimenti lì non compare nella UI e il worker rifiuta un processo che la chiede. Da CLI funziona comunque: etl run risolve per nome senza passare dal filtro.

Come si lancia ​

bash
pnpm etl run test-posta

Che cosa guardare, in ordine:

  1. Il report: extracted sono le mail lette, written le mail inviate (quindi 1, non il numero di file scritti), rejected gli scarti. Se extracted è 0 la casella era vuota e il resto non dimostra nulla. I file scritti si contano in nodes[], alla voce salva-eml.
  2. La cartella locale: un .eml per messaggio, apribile con un client di posta.
  3. La mail: arriva al destinatario configurato, con mittente, data, numero di allegati e percorso per ciascun messaggio (fino a 50). Se gli accenti si leggono, la codifica ha attraversato IMAP e SMTP intatta. Una riga ATTENZIONE: non interpretabile segnala un messaggio che il parser ha rifiutato: l'.eml è stato comunque salvato, ma i suoi campi nel riepilogo sono vuoti perché non si sono potuti leggere, non perché la mail fosse vuota.

Test ​

FileChe cosa copre
config.test.tsVariabili obbligatorie, default, MAX_MESSAGES non valido, tutte le mancanti in ConfigError.mancanti, configurazione d'esempio del piano
salvataggio.test.tsSanificazione dell'oggetto (compreso ../../etc/passwd), zero padding, idempotenza per uid, eml nullo, motivo di parsing riportato
notifica.test.tsCorpo della mail, troncamento a 50 righe, riga di attenzione, assenza di chiave
pipeline.test.tsEnd-to-end con arraySource/arraySink: N messaggi → una mail, casella vuota, messaggio illeggibile che ferma la run, topologia
run.test.tsRegistrazione, ricaduta per istanza (cartella locale mancante, istanza IMAP rotta), variabile e origine per riga, riga credenziale, configurazione della pipeline nel piano, piano deterministico senza .env, run fail-fast con l'elenco completo

Versioni ​

La storia della pipeline, dalla versione più recente: la voce di changelog di ogni versione (src/pipelines/test-posta/changelog.ts) e, sotto, cosa è cambiato nel piano rispetto alla precedente. Vedi versioni di pipeline.

v32026-10-09 · ETL-193, ETL-194, ETL-195, ETL-202
Modifiche
  • Nessun cambio di comportamento: il piano è identico alla v2. Gli import seguono l'infrastruttura (changelog, configurazione delle istanze, report di volume) uscita da `src/pipelines/` (ADR-0093 §1), e la pipeline si dichiara nel catalogo di `etl.config.ts` invece che in un registro (ADR-0093 §2)
  • Porta il marchio di pipeline pronta del core, che il caricamento del catalogo riconosce (ADR-0094 §2): il piano non cambia
  • Dichiara le variabili d'ambiente che legge (`environment`) per `etl env check`, dagli stessi manifest della run: casella IMAP, server SMTP, identità OAuth2 se la casella la usa, e `TEST_POSTA_*` (ADR-0094 §1). Il piano non cambia

Il piano non cambia rispetto alla v2.

v22026-10-05 · ETL-150, ETL-152, ETL-154
Aggiunte
  • Variabile e origine (env, default, mancante, non impostata) su ogni riga dei connettori IMAP, SMTP e della cartella locale (ADR-0089)
  • Riga `credenziale` con la variante in uso e il suo stato, mai il valore
  • Configurazione della pipeline nel piano: tutte le variabili `TEST_POSTA_*` con valore e origine, compreso il destinatario che nessun nodo mostra
Modifiche
  • Il piano ricade sulla configurazione d'esempio per istanza di connettore, non per l'intero piano: una cartella locale mancante non nasconde IMAP e SMTP
  • Host e porta sono righe separate
  • Con un utente IMAP mancante, la riga `credenziale` lo dice; con OAuth2, dice se l'identità che rinnova il token è completa

Il piano rispetto alla v1:

Nodi
  • ~postaconnettore
  • ~salva-emlconnettore
  • ~notificaconnettore
v12026-10-05 · ETL-120
Aggiunte
  • Versione iniziale sotto versionamento (ADR-0086)

Prima versione: non c'è una versione precedente da confrontare.

Noeva è un marchio registrato di 4D S.R.L.