using CaseFramework.Api; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("CaseFramework"))); var app = builder.Build(); // Self-provision schema against an empty Postgres database on first boot. using (var scope = app.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); await dbContext.Database.EnsureCreatedAsync(); } app.MapPost("/cases", async (CreateCaseRequest request, CaseDbContext db) => { var now = DateTimeOffset.UtcNow; var caseEntity = new CaseEntity { Id = Guid.NewGuid(), CaseTypeCode = request.CaseTypeCode, ExternalReference = request.ExternalReference, ProcessStatus = ProcessStatus.InBehandeling, Participants = request.Participants?.ToList() ?? new List(), }; caseEntity.TimelineEntries.Add(new TimelineEntryEntity { Id = Guid.NewGuid(), CaseId = caseEntity.Id, At = now, Kind = TimelineKind.CaseCreated, Description = $"Case created with type '{caseEntity.CaseTypeCode}'.", }); db.Cases.Add(caseEntity); await db.SaveChangesAsync(); return Results.Ok(new CreateCaseResponse(caseEntity.Id, caseEntity.ProcessStatus)); }); app.MapGet("/cases/{id:guid}", async (Guid id, CaseDbContext db) => { var caseEntity = await db.Cases.AsNoTracking().FirstOrDefaultAsync(c => c.Id == id); if (caseEntity is null) { return Results.NotFound(); } return Results.Ok(new CaseResponse( caseEntity.Id, caseEntity.CaseTypeCode, caseEntity.ExternalReference, caseEntity.ProcessStatus, caseEntity.Participants)); }); app.MapGet("/cases/{id:guid}/timeline", async (Guid id, CaseDbContext db) => { var caseExists = await db.Cases.AnyAsync(c => c.Id == id); if (!caseExists) { return Results.NotFound(); } var entries = await db.TimelineEntries .AsNoTracking() .Where(t => t.CaseId == id) .OrderBy(t => t.At) .Select(t => new TimelineEntryResponse(t.At, t.Kind, t.Description)) .ToListAsync(); return Results.Ok(new TimelineResponse(entries)); }); app.MapPost("/cases/{id:guid}/tasks", async (Guid id, CreateTaskRequest request, CaseDbContext db) => { var caseEntity = await db.Cases.FirstOrDefaultAsync(c => c.Id == id); if (caseEntity is null) { return Results.NotFound(); } var task = new CaseTaskEntity { Id = Guid.NewGuid(), CaseId = id, Code = request.Code, Description = request.Description, IsOpen = true, }; db.Tasks.Add(task); db.TimelineEntries.Add(new TimelineEntryEntity { Id = Guid.NewGuid(), CaseId = id, At = DateTimeOffset.UtcNow, Kind = TimelineKind.TaskOpened, Description = $"Task '{task.Code}' opened: {task.Description}", }); await db.SaveChangesAsync(); return Results.Ok(new CreateTaskResponse(task.Id, task.IsOpen)); }); app.MapPost("/cases/{id:guid}/tasks/{taskId:guid}/complete", async (Guid id, Guid taskId, CaseDbContext db) => { var caseExists = await db.Cases.AnyAsync(c => c.Id == id); if (!caseExists) { return Results.NotFound(); } var task = await db.Tasks.FirstOrDefaultAsync(t => t.Id == taskId && t.CaseId == id); if (task is null) { return Results.NotFound(); } task.IsOpen = false; db.TimelineEntries.Add(new TimelineEntryEntity { Id = Guid.NewGuid(), CaseId = id, At = DateTimeOffset.UtcNow, Kind = TimelineKind.TaskCompleted, Description = $"Task '{task.Code}' completed.", }); await db.SaveChangesAsync(); return Results.NoContent(); }); app.MapPost("/cases/{id:guid}/closure-request", async (Guid id, CaseDbContext db) => { var caseEntity = await db.Cases.FirstOrDefaultAsync(c => c.Id == id); if (caseEntity is null) { return Results.NotFound(); } var hasOpenTasks = await db.Tasks.AnyAsync(t => t.CaseId == id && t.IsOpen); if (hasOpenTasks) { return Results.Conflict(); } caseEntity.ProcessStatus = ProcessStatus.Afgesloten; db.TimelineEntries.Add(new TimelineEntryEntity { Id = Guid.NewGuid(), CaseId = id, At = DateTimeOffset.UtcNow, Kind = TimelineKind.CaseClosed, Description = "Case closed.", }); await db.SaveChangesAsync(); return Results.NoContent(); }); app.Run(); internal record CreateCaseRequest(string CaseTypeCode, string ExternalReference, List? Participants); internal record CreateCaseResponse(Guid Id, string ProcessStatus); internal record CaseResponse( Guid Id, string CaseTypeCode, string ExternalReference, string ProcessStatus, List Participants); internal record CreateTaskRequest(string Code, string Description); internal record CreateTaskResponse(Guid TaskId, bool Open); internal record TimelineEntryResponse(DateTimeOffset At, string Kind, string Description); internal record TimelineResponse(List Entries);