using Microsoft.AspNetCore.Http.HttpResults; using MinAttest.Contracts.Attests; using MinAttest.Contracts.Employers; using MediatR; using MinAttest.Application.Features.Employers.Commands; using MinAttest.Application.Features.Employers.Queries; using MinAttest.Application.Features.Attests.Commands; using MinAttest.Application.Features.Attests.Queries; namespace MinAttest.Api.Features.Employer; public static class Employers { public static RouteGroupBuilder MapEmployer(this RouteGroupBuilder group) { var employers = group.MapGroup("/employers"); employers.MapPost("/", UpsertEmployer) .WithName("UpsertEmployer") .WithOpenApi(); employers.MapGet("/", ListEmployers) .WithName("ListEmployers") .WithOpenApi(); employers.MapGet("/{id:guid}", GetEmployer) .WithName("GetEmployer") .WithOpenApi(); employers.MapGet("/{employerId:guid}/users", ListEmployerUsers) .WithName("ListEmployerUsers") .WithOpenApi(); employers.MapGet("/{employerId:guid}/users/{userId:guid}", GetEmployerUser) .WithName("GetEmployerUser") .WithOpenApi(); employers.MapPost("/{employerId:guid}/users", UpsertEmployerUser) .WithName("UpsertEmployerUser") .WithOpenApi(); employers.MapDelete("/{employerId:guid}/users/{userId:guid}", DeleteEmployerUser) .WithName("DeleteEmployerUser") .WithOpenApi(); employers.MapPost("/{id:guid}/attests", IssueAttestForEmployer) .WithName("IssueAttestForEmployer") .WithOpenApi(); employers.MapGet("/{id:guid}/attests", ListAttestsForEmployer) .WithName("ListAttestsForEmployer") .WithOpenApi(); employers.MapGet("/{id:guid}/attests/{attestId:guid}/download", DownloadAttestForEmployer) .WithName("DownloadAttestForEmployer") .WithOpenApi(); return group; } private static async Task IssueAttestForEmployer(Guid id, EmployerAttestUploadRequest req, IMediator mediator, CancellationToken ct) { var createdId = await mediator.Send(new EmployerIssueAttestCommand(id, req), ct); if (createdId is null) return Results.NotFound(); return Results.Created($"/api/v1/employers/{id}/attests/{createdId}", new { attestId = createdId }); } private static async Task ListAttestsForEmployer(Guid id, int? take, IMediator mediator, CancellationToken ct) { var items = await mediator.Send(new ListEmployerAttestsQuery(id, take), ct); return Results.Ok(items); } private static async Task DownloadAttestForEmployer(Guid id, Guid attestId, bool? inline, IMediator mediator, CancellationToken ct) { var content = await mediator.Send(new GetAttestContentQuery(attestId), ct); if (content is null) return Results.NotFound(); var contentType = NormalizeContentType(content.Content, content.ContentType); var fileName = NormalizeFileName(content.FileName, attestId, contentType); if (inline == true) { return Results.File(content.Content, contentType); } return Results.File(content.Content, contentType, fileName); } private static string NormalizeContentType(byte[] data, string? original) { var ct = string.IsNullOrWhiteSpace(original) || string.Equals(original, "application/octet-stream", StringComparison.OrdinalIgnoreCase) ? null : original; if (ct is null) { if (data is { Length: >= 4 } && data[0] == 0x25 && data[1] == 0x50 && data[2] == 0x44 && data[3] == 0x46) { return "application/pdf"; } } return ct ?? "application/pdf"; } private static string NormalizeFileName(string? name, Guid attestId, string contentType) { var n = string.IsNullOrWhiteSpace(name) ? $"attest-{attestId}" : name!; if (string.Equals(contentType, "application/pdf", StringComparison.OrdinalIgnoreCase) && !n.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) { n += ".pdf"; } return n; } private static async Task UpsertEmployer(EmployerUpsertRequest req, IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new UpsertEmployerCommand(req.OrgNumber, req.Name), ct); return Results.Ok(resp); } private static async Task ListEmployers(IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new ListEmployersQuery(), ct); return Results.Ok(resp); } private static async Task, NotFound>> GetEmployer(Guid id, IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new GetEmployerQuery(id), ct); if (resp is null) return TypedResults.NotFound(); return TypedResults.Ok(resp); } private static async Task ListEmployerUsers(Guid employerId, IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new ListEmployerUsersQuery(employerId), ct); return Results.Ok(resp); } private static async Task, NotFound>> GetEmployerUser(Guid employerId, Guid userId, IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new GetEmployerUserQuery(employerId, userId), ct); if (resp is null) return TypedResults.NotFound(); return TypedResults.Ok(resp); } private static async Task, NotFound, BadRequest>> UpsertEmployerUser(Guid employerId, EmployerUserUpsertRequest req, IMediator mediator, CancellationToken ct) { var resp = await mediator.Send(new UpsertEmployerUserCommand(employerId, req.ExternalObjectId, req.Email, req.Name, req.Role), ct); if (resp is null) return TypedResults.NotFound(); return TypedResults.Ok(resp); } private static async Task> DeleteEmployerUser(Guid employerId, Guid userId, IMediator mediator, CancellationToken ct) { var ok = await mediator.Send(new DeleteEmployerUserCommand(employerId, userId), ct); if (!ok) return TypedResults.NotFound(); return TypedResults.NoContent(); } }