From 9d90f9120bdb3a90d6f310b80e6c23ed99a34c28 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 30 Aug 2026 16:50:40 +0200 Subject: [PATCH] Implemented GetProjectIndicatorsEndpoint --- .../Evidence/GetMilestoneEvidenceEndpoint.cs | 32 ++++++++ .../Evidence/GetProjectEvidenceEndpoint.cs | 32 ++++++++ .../Evidence/GetProjectIndicatorsEndpoint.cs | 32 ++++++++ .../Services/Evidence/EvidenceService.cs | 74 +++++++++++++++++++ 4 files changed, 170 insertions(+) create mode 100644 PostFundManagement.Api/Endpoints/Evidence/GetMilestoneEvidenceEndpoint.cs create mode 100644 PostFundManagement.Api/Endpoints/Evidence/GetProjectEvidenceEndpoint.cs create mode 100644 PostFundManagement.Api/Endpoints/Evidence/GetProjectIndicatorsEndpoint.cs diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetMilestoneEvidenceEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetMilestoneEvidenceEndpoint.cs new file mode 100644 index 0000000..2381905 --- /dev/null +++ b/PostFundManagement.Api/Endpoints/Evidence/GetMilestoneEvidenceEndpoint.cs @@ -0,0 +1,32 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Evidence; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Security; + +namespace PostFundManagement.Api.Endpoints.Evidence; + +[ApiVersionTarget(1)] +public class GetMilestoneEvidenceEndpoint : IEndpoint +{ + public void Map(IEndpointRouteBuilder builder) + { + builder.MapGet("api/evidence/milestone/{milestoneId}", async (string milestoneId, EvidenceService service, + CancellationToken cancellationToken = default) => + { + var result = await service.GetMilestoneEvidencesAsync(milestoneId, cancellationToken); + + return result.IsSuccess + ? Results.Ok(result.Value) + : Results.NotFound(); + }) + .WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone") + .WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName()) + .MapToApiVersion(new ApiVersion(1)) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status401Unauthorized) + .WithTags(EndpointTags.Evidence) + .CacheOutput(); + } +} diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetProjectEvidenceEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetProjectEvidenceEndpoint.cs new file mode 100644 index 0000000..474fe46 --- /dev/null +++ b/PostFundManagement.Api/Endpoints/Evidence/GetProjectEvidenceEndpoint.cs @@ -0,0 +1,32 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Evidence; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Security; + +namespace PostFundManagement.Api.Endpoints.Evidence; + +[ApiVersionTarget(1)] +public class GetProjectEvidenceEndpoint : IEndpoint +{ + public void Map(IEndpointRouteBuilder builder) + { + builder.MapGet("api/evidence/milestone/{projectId}", async (string projectId, EvidenceService service, + CancellationToken cancellationToken = default) => + { + var result = await service.GetProjectEvidencesAsync(projectId, cancellationToken); + + return result.IsSuccess + ? Results.Ok(result.Value) + : Results.NotFound(); + }) + .WithDescription("Get a list of all uploaded evidence metadata for a specific project") + .WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName()) + .MapToApiVersion(new ApiVersion(1)) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status401Unauthorized) + .WithTags(EndpointTags.Evidence) + .CacheOutput(); + } +} diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetProjectIndicatorsEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetProjectIndicatorsEndpoint.cs new file mode 100644 index 0000000..097ee43 --- /dev/null +++ b/PostFundManagement.Api/Endpoints/Evidence/GetProjectIndicatorsEndpoint.cs @@ -0,0 +1,32 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Application.Services.Evidence; +using PostFundManagement.Domain.Abstractions; +using PostFundManagement.Domain.Extensions; +using PostFundManagement.Domain.Models; +using PostFundManagement.Domain.Security; + +namespace PostFundManagement.Api.Endpoints.Evidence; + +[ApiVersionTarget(1)] +public class GetProjectIndicatorsEndpoint : IEndpoint +{ + public void Map(IEndpointRouteBuilder builder) + { + builder.MapGet("api/evidence/indicators/project/{projectId}", async (string projectId, EvidenceService service, + CancellationToken cancellationToken = default) => + { + var result = await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken); + + return result.IsSuccess + ? Results.Ok(result.Value) + : Results.NotFound(); + }) + .WithDescription("Get get a detailed list of project indicators") + .WithName(typeof(GetProjectIndicatorsEndpoint).ToEndpointName()) + .MapToApiVersion(new ApiVersion(1)) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status401Unauthorized) + .WithTags(EndpointTags.Evidence); + } +} diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs index a070297..39944c9 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs @@ -300,4 +300,78 @@ public sealed class EvidenceService(IDbContextFactory cont return Result.Fail(new Error(ex.Message).CausedBy(ex)); } } + + public async ValueTask> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) + { + try + { + var decodeResult = hashService.DecodeLongIdHash(milestoneIdHash); + + if (decodeResult.IsFailed) + return Result.Fail(decodeResult.Errors); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var evidences = await context.Evidences.AsNoTracking() + .Where(e => e.MilestoneId == decodeResult.Value) + .OrderByDescending(o => o.CreatedAt) + .ThenByDescending(o => o.UpdatedAt) + .ToListAsync(cancellationToken); + + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) + { + try + { + var decodeResult = hashService.DecodeLongIdHash(projectIdHash); + + if (decodeResult.IsFailed) + return Result.Fail(decodeResult.Errors); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var evidences = await context.Evidences.AsNoTracking() + .Where(e => e.ProjectId == decodeResult.Value) + .OrderByDescending(o => o.CreatedAt) + .ThenBy(o => o.UpdatedAt) + .ToListAsync(cancellationToken); + + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken) + { + try + { + var decodeResult = hashService.DecodeLongIdHash(projectIdHash); + + if (decodeResult.IsFailed) + return Result.Fail(decodeResult.Errors); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var evidences = await context.Indicators.AsNoTracking() + .Where(e => e.ProjectId == decodeResult.Value) + .OrderByDescending(o => o.Id) + .ToListAsync(cancellationToken); + + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } }