handlers/csv.ps1
|
<#
.SYNOPSIS CSV Handler — schreibt Records in eine CSV-Datei. .PARAMETER Records Array von Hashtables aus dbo.WebhookEvents .PARAMETER Config Ziel-Konfiguration aus endpoints/*.yaml .PARAMETER Endpoint Name des Endpunkts .PARAMETER ScriptDir Pfad zum Dispatcher-Verzeichnis (für Helper-Zugriff) .OUTPUTS Array von @{Id=[long]; Success=[bool]; Error=[string]} YAML-Konfiguration: type: csv path: "D:\Exports\{endpoint}\{datetime}.csv" delimiter: ";" # optional, default: ; encoding: UTF8 # optional, default: UTF8 (UTF8, UTF8BOM, ASCII, Unicode) append: false # optional — true: an bestehende Datei anhängen includeHeaders: true # optional — Kopfzeile schreiben (nur bei neuer Datei) columns: # optional — wenn leer: alle Spalten + payload-Felder - field: id header: EventId - field: received_at header: Datum - field: payload.email header: Email - field: payload.event header: Event - field: "$" header: RawPayload #> param( [object[]] $Records, [hashtable] $Config, [string] $Endpoint, [string] $ScriptDir ) Set-StrictMode -Version Latest # Hilfsfunktionen aus dispatch.ps1 sind im gleichen Scope verfügbar # (dot-sourced vom Hauptskript über & Aufruf im selben Runspace) # ------------------------------------------------------------ # Konfiguration auslesen # ------------------------------------------------------------ $delimiter = if ($Config['delimiter']) { $Config['delimiter'] } else { ';' } $encodingName = if ($Config['encoding']) { $Config['encoding'] } else { 'UTF8' } $append = if ($null -ne $Config['append']) { [bool]$Config['append'] } else { $false } $includeHeaders = if ($null -ne $Config['includeHeaders']) { [bool]$Config['includeHeaders'] } else { $true } $columns = $Config['columns'] # kann $null sein # Encoding-Objekt $encoding = switch ($encodingName.ToUpper()) { 'UTF8BOM' { [System.Text.UTF8Encoding]::new($true) } 'ASCII' { [System.Text.ASCIIEncoding]::new() } 'UNICODE' { [System.Text.UnicodeEncoding]::new() } default { [System.Text.UTF8Encoding]::new($false) } # UTF8 ohne BOM } # Pfad auflösen $outPath = Resolve-PathTemplate -Template $Config['path'] -Endpoint $Endpoint -Target $Config['name'] $outDir = Split-Path $outPath -Parent $null = New-Item -ItemType Directory -Path $outDir -Force # Spalten ermitteln — explizit oder auto if ($columns -and $columns.Count -gt 0) { $colDefs = $columns | ForEach-Object { @{ Field = $_.field; Header = if ($_.header) { $_.header } else { $_.field } } } } else { # Automatisch: alle DB-Spalten + payload als letzte Spalte $colDefs = @( @{ Field = 'id'; Header = 'id' }, @{ Field = 'endpoint'; Header = 'endpoint' }, @{ Field = 'source_ip'; Header = 'source_ip' }, @{ Field = 'received_at'; Header = 'received_at' }, @{ Field = 'inserted_at'; Header = 'inserted_at' }, @{ Field = '$'; Header = 'payload' } ) } # ------------------------------------------------------------ # Datei schreiben # ------------------------------------------------------------ $fileExists = Test-Path $outPath $writeMode = if ($append -and $fileExists) { [System.IO.FileMode]::Append } else { [System.IO.FileMode]::Create } $stream = [System.IO.FileStream]::new($outPath, $writeMode, [System.IO.FileAccess]::Write) $writer = [System.IO.StreamWriter]::new($stream, $encoding) try { # Header — nur bei neuer Datei oder wenn nicht append if ($includeHeaders -and (-not $append -or -not $fileExists)) { $headerLine = ($colDefs | ForEach-Object { Escape-CsvField $_.Header $delimiter }) -join $delimiter $writer.WriteLine($headerLine) } $results = [System.Collections.Generic.List[hashtable]]::new() foreach ($record in $Records) { try { $values = $colDefs | ForEach-Object { $val = Resolve-Field -Record $record -FieldExpr $_.Field Escape-CsvField ($val ?? '') $delimiter } $writer.WriteLine($values -join $delimiter) $results.Add(@{ Id = $record['id']; Success = $true; Error = $null }) } catch { $results.Add(@{ Id = $record['id']; Success = $false; Error = $_.Exception.Message }) } } } finally { $writer.Flush() $writer.Close() $stream.Close() } Write-Log "[$Endpoint][CSV] Geschrieben nach: $outPath" -Level INFO return $results # ------------------------------------------------------------ # CSV-Feld escapen (RFC 4180) # ------------------------------------------------------------ function Escape-CsvField([string]$value, [string]$delim) { if ($value -match "[`"$([regex]::Escape($delim))\r\n]") { return '"' + $value.Replace('"', '""') + '"' } return $value } |