Upload feature (a): BFF endpoints + category config + tests
- Domain/Documents: server-owned category config per wizard + authoritative
type/size validation (DocumentRules).
- Data/DocumentStore: in-memory metadata store (no file bytes/PII) + audit log;
user delete (owner-scoped, 409 once linked), admin delete (role seam via
X-Admin header), link-on-submit, poll-by-localId status.
- Program.cs: GET /uploads/categories, POST /uploads (multipart, excluded from
OpenAPI — hand-written on FE), GET /uploads/status, DELETE /uploads/{id},
DELETE /admin/uploads/{id}. Submit links digital docs + records post-delivery.
- Contracts extended (DocumentRefDto on registratie/herregistratie submit);
regenerated NSwag client + swagger.json (drift check stays green).
- Tests: 16 new (endpoints + DocumentRules); dotnet test 44/44.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
namespace BigRegister.Domain.Documents;
|
||||
|
||||
/// <summary>
|
||||
/// SERVER-OWNED document category config. The frontend renders these; it never
|
||||
/// hardcodes file types, size limits, labels, required-ness or the post-delivery
|
||||
/// flag. Adding a new category or changing a rule is a backend-only change.
|
||||
/// </summary>
|
||||
public sealed record DocumentCategory(
|
||||
string CategoryId,
|
||||
string Label,
|
||||
string Description,
|
||||
bool Required,
|
||||
IReadOnlyList<string> AcceptedTypes,
|
||||
int MaxSizeMb,
|
||||
bool Multiple,
|
||||
bool AllowPostDelivery);
|
||||
|
||||
public static class DocumentRules
|
||||
{
|
||||
private static readonly string[] Pdf = { "application/pdf" };
|
||||
private static readonly string[] PdfImage = { "application/pdf", "image/jpeg", "image/png" };
|
||||
|
||||
/// <summary>The categories that apply to a given wizard/step.</summary>
|
||||
public static IReadOnlyList<DocumentCategory> CategoriesFor(string wizardId) => wizardId switch
|
||||
{
|
||||
"registratie" => new[]
|
||||
{
|
||||
new DocumentCategory("diploma", "Diplomabewijs",
|
||||
"Upload uw diploma als PDF-bestand.", true, Pdf, 10, false, false),
|
||||
new DocumentCategory("identiteit", "Identiteitsbewijs",
|
||||
"Upload een kopie van uw paspoort of ID-kaart.", true, PdfImage, 10, false, true),
|
||||
},
|
||||
"herregistratie" => new[]
|
||||
{
|
||||
new DocumentCategory("werkervaring", "Bewijs van werkervaring",
|
||||
"Upload bewijs van uw gewerkte uren (bijv. een werkgeversverklaring).", true, Pdf, 10, true, true),
|
||||
new DocumentCategory("nascholing", "Nascholingscertificaten",
|
||||
"Upload uw nascholingscertificaten (optioneel).", false, PdfImage, 10, true, true),
|
||||
},
|
||||
_ => Array.Empty<DocumentCategory>(),
|
||||
};
|
||||
|
||||
public static DocumentCategory? Find(string wizardId, string categoryId) =>
|
||||
CategoriesFor(wizardId).FirstOrDefault(c => c.CategoryId == categoryId);
|
||||
|
||||
/// <summary>
|
||||
/// Authoritative upload validation (the client check is UX-only). Returns a
|
||||
/// rejection reason, or null when the file is acceptable.
|
||||
/// </summary>
|
||||
public static string? RejectUpload(DocumentCategory? category, string contentType, long sizeBytes)
|
||||
{
|
||||
if (category is null) return "Onbekende documentcategorie.";
|
||||
if (!category.AcceptedTypes.Contains(contentType))
|
||||
return $"Bestandstype niet toegestaan voor {category.Label}.";
|
||||
if (sizeBytes > (long)category.MaxSizeMb * 1024 * 1024)
|
||||
return $"Bestand is groter dan {category.MaxSizeMb} MB.";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user