Add local blob-triggered CSV validation with Azurite

This commit is contained in:
admin committed 2026-09-07 15:34:58 +02:00
1 parent b72aa278a5
commit 2075bcb2c3
7 files changed
+153 -27

No files matched your search

+3
View File
@@ -6,4 +6,7 @@
<Folder Name="/src/ImportAssist.Functions/">
<Project Path="src/ImportAssist.Functions/ImportAssist_Functions.csproj" />
</Folder>
<Folder Name="/tools/">
<Project Path="tools/ImportAssist.Upload/ImportAssist.Upload.csproj" />
</Folder>
</Solution>
+7 -27
View File
@@ -17,39 +17,19 @@ if (!File.Exists(path))
return;
}
var lines = File.ReadAllLines(path);
IReadOnlyList<InvoiceRow> rows;
if (lines.Length == 0 ||
lines[0] != "InvoiceId;CustomerId;Amount;Currency")
try
{
Console.Error.WriteLine(
"Die Datei ist leer oder die Kopfzeile stimmt nicht.");
rows = InvoiceCsvParser.Parse(File.ReadAllText(path));
}
catch (FormatException exception)
{
Console.Error.WriteLine(exception.Message);
Environment.ExitCode = 1;
return;
}
var rows = new List<InvoiceRow>();
for (var index = 1; index < lines.Length; index++)
{
var line = lines[index];
var columns = line.Split(';');
if (line.Contains('"') || columns.Length != 4)
{
Console.Error.WriteLine(
$"Zeile {index + 1}: Erwartet werden vier Spalten ohne Anführungszeichen.");
Environment.ExitCode = 1;
return;
}
rows.Add(new InvoiceRow(
columns[0],
columns[1],
columns[2],
columns[3]));
}
foreach (var row in rows)
{
var errors = InvoiceValidator.Validate(row);
+41
View File
@@ -0,0 +1,41 @@
namespace ImportAssist.Core;
public static class InvoiceCsvParser
{
public static IReadOnlyList<InvoiceRow> Parse(string csv)
{
using var reader = new StringReader(csv);
var header = reader.ReadLine()?.TrimStart('\uFEFF');
if (header != "InvoiceId;CustomerId;Amount;Currency")
{
throw new FormatException(
"Die Datei ist leer oder die Kopfzeile stimmt nicht.");
}
var rows = new List<InvoiceRow>();
var lineNumber = 1;
while (reader.ReadLine() is { } line)
{
lineNumber++;
var columns = line.Split(';');
if (line.Contains('"') || columns.Length != 4)
{
throw new FormatException(
$"Zeile {lineNumber}: Erwartet werden vier Spalten ohne Anführungszeichen.");
}
rows.Add(new InvoiceRow(
columns[0],
columns[1],
columns[2],
columns[3]));
}
return rows;
}
}
@@ -11,6 +11,7 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs" Version="6.8.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.OpenTelemetry" Version="1.2.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<PackageReference Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.7.0" />
@@ -0,0 +1,66 @@
using ImportAssist.Core;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace ImportAssist_Functions;
public sealed class InvoiceBlobTrigger(
ILogger<InvoiceBlobTrigger> logger)
{
[Function("InvoiceBlobTrigger")]
public void Run(
[BlobTrigger(
"imports/{name}",
Connection = "AzureWebJobsStorage")]
string content,
string name)
{
IReadOnlyList<InvoiceRow> rows;
try
{
rows = InvoiceCsvParser.Parse(content);
}
catch (FormatException exception)
{
logger.LogWarning(
"Datei {BlobName} wegen CSV-Formatfehler abgewiesen: {Reason}",
name,
exception.Message);
return;
}
var validCount = 0;
foreach (var row in rows)
{
var errors = InvoiceValidator.Validate(row);
if (errors.Count == 0)
{
validCount++;
}
var invoiceId = string.IsNullOrWhiteSpace(row.InvoiceId)
? "(ohne Rechnungsnummer)"
: row.InvoiceId;
logger.LogInformation(
"{BlobName}: {InvoiceId}: {Result}",
name,
invoiceId,
errors.Count == 0
? "Gültig"
: string.Join("; ", errors));
}
logger.LogInformation(
"Dateiprüfung abgeschlossen: {BlobName}, " +
"{TotalCount} Rechnungen, {ValidCount} gültig, {InvalidCount} fehlerhaft.",
name,
rows.Count,
validCount,
rows.Count - validCount);
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.29.2" />
</ItemGroup>
</Project>
+21
View File
@@ -0,0 +1,21 @@
using Azure.Storage.Blobs;
if (args.Length != 1 || !File.Exists(args[0]))
{
Console.Error.WriteLine("Bitte eine vorhandene Datei angeben.");
Environment.ExitCode = 1;
return;
}
var container = new BlobContainerClient(
"UseDevelopmentStorage=true",
"imports");
await container.CreateIfNotExistsAsync();
var blobName = $"{Guid.NewGuid():N}-{Path.GetFileName(args[0])}";
var blob = container.GetBlobClient(blobName);
await blob.UploadAsync(args[0]);
Console.WriteLine($"Hochgeladen: imports/{blobName}");