9 Commits
237 changed files with 2169 additions and 48450 deletions
-21
View File
@@ -1,21 +0,0 @@
# Exclude build output and caches
**/bin/
**/obj/
**/out/
.vs/
.vscode/
# Exclude git internal files
.git/
.gitignore
# Exclude local environment/secret files
.env
*.env
*.user
*.suo
# Exclude node modules and temporary logs
**/node_modules/
**/dist/
*.log
-3
View File
@@ -43,9 +43,6 @@ dotnet_diagnostic.MA0004.severity = none
[**/*{Enums,Records}.cs] [**/*{Enums,Records}.cs]
dotnet_diagnostic.MA0048.severity = none dotnet_diagnostic.MA0048.severity = none
[*.{cs,vb}]
dotnet_diagnostic.IDE0039.severity = none
#### Core EditorConfig Options #### #### Core EditorConfig Options ####
# Indentation and spacing # Indentation and spacing
+2 -11
View File
@@ -11,7 +11,6 @@
*.sln.docstates *.sln.docstates
*.env *.env
*.env.json *.env.json
certificates/
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
@@ -52,8 +51,6 @@ bld/
# Build results on 'Bin' directories # Build results on 'Bin' directories
**/[Bb]in/* **/[Bb]in/*
dist/assets/
dist/
# Uncomment if you have tasks that rely on *.refresh files to move binaries # Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736) # (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh #!**/[Bb]in/*.refresh
@@ -61,7 +58,8 @@ dist/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
.vscode/ .vscode/
bin/
certificates/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/ #wwwroot/
@@ -349,7 +347,6 @@ _Pvt_Extensions
# Paket dependency manager # Paket dependency manager
**/.paket/paket.exe **/.paket/paket.exe
**/.paket/paket.exe
paket-files/ paket-files/
# FAKE - F# Make # FAKE - F# Make
@@ -435,9 +432,3 @@ FodyWeavers.xsd
*.msix *.msix
*.msm *.msm
*.msp *.msp
/PostFundManagement.ConsoleUI/dist/config.json
/PostFundManagement.ConsoleUI/dist/env-config.js
/PostFundManagement.ConsoleUI/dist/assets/index-BaOzkVJi.css
/PostFundManagement.ConsoleUI/dist/assets/index-BU8qSe7k.js
/PostFundManagement.ConsoleUI/dist/favicon.svg
/PostFundManagement.ConsoleUI/dist/index.html
-36
View File
@@ -1,36 +0,0 @@
# Stage 1: Runtime base
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER app
WORKDIR /app
EXPOSE 8080
# Stage 2: Build & Restore
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy csproj files for layer caching
COPY ["PostFundManagement.Api/PostFundManagement.Api.csproj", "PostFundManagement.Api/"]
COPY ["PostFundManagement.Application/PostFundManagement.Application.csproj", "PostFundManagement.Application/"]
COPY ["PostFundManagement.Domain/PostFundManagement.Domain.csproj", "PostFundManagement.Domain/"]
COPY ["PostFundManagement.Infrastructure/PostFundManagement.Infrastructure.csproj", "PostFundManagement.Infrastructure/"]
# Restore packages
RUN dotnet restore "PostFundManagement.Api/PostFundManagement.Api.csproj"
# Copy full source code
COPY . .
# Build from the project folder using relative filename
WORKDIR "/src/PostFundManagement.Api"
RUN dotnet build "PostFundManagement.Api.csproj" -c Release -o /app/build
# Stage 3: Publish
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "PostFundManagement.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Stage 4: Final Runtime
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "PostFundManagement.Api.dll"]
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Communications;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Communications; namespace PostFundManagement.Api.Endpoints.Communications;
@@ -14,22 +12,21 @@ public sealed class GetApplicantInviteEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/communications/invites/applicant/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service, builder.MapGet("api/communications/invites/applicant/{userId:Guid}", async (Guid userId, CommunicationsService service,
HttpContext context, CancellationToken cancellationToken = default) => HttpContext context, CancellationToken cancellationToken = default) =>
await service.GetApplicantInviteAsync(applicantUserId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var invite } => Results.Ok(invite), var result = await service.GetApplicantInviteAsync(userId, cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()), return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Gets an applicant's invite") .WithDescription("Gets an applicant's invite")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName()) .WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Invite>(StatusCodes.Status200OK) .Produces<Invite>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Communications) .WithTags(EndpointTags.Communications)
.CacheOutput(); .CacheOutput();
} }
@@ -1,36 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Communications;
[ApiVersionTarget(1)]
public sealed class GetApplicantInvitesEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/communications/invites/{applicantUserId:Guid}", async (Guid applicantUserId, ICommunicationsService service,
HttpContext context, CancellationToken cancellationToken = default) =>
await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken) switch
{
{ IsSuccess: true, Value: var invite } => Results.Ok(invite),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()),
})
.WithDescription("Gets an applicant's invite list")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetApplicantInvitesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Invite[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Communications)
.CacheOutput();
}
}
@@ -1,34 +1,34 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Communications;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using static PostFundManagement.Application.Extensions.General; using static PostFundManagement.Application.Extensions.General;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Communications; namespace PostFundManagement.Api.Endpoints.Communications;
[ApiVersionTarget(1)] [ApiVersionTarget(1)]
public sealed class GetInvitesEndpoint : IEndpoint public class GetInvitesEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/communications/invites/{page:int}/{pageSize:int}", async (ICommunicationsService service, builder.MapGet("api/communications/invites/{page:int}/{pageSize:int}", async (CommunicationsService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetInvitesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var invites } => Results.Ok(invites), var result = await service.GetInvitesAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a list of sent invitations") .WithDescription("Get a list of sent invitations")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetInvitesEndpoint).ToEndpointName()) .WithName(typeof(GetInvitesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Invite[]>(StatusCodes.Status200OK) .Produces<Invite[]>(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Communications) .WithTags(EndpointTags.Communications)
.CacheOutput(); .CacheOutput();
} }
@@ -1,35 +1,30 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Communications; using PostFundManagement.Application.Services.Communications;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Communications; namespace PostFundManagement.Api.Endpoints.Communications;
[ApiVersionTarget(1)] [ApiVersionTarget(1)]
public sealed class SendInviteEndpoint : IEndpoint public class SendInviteEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/communications/invite", async (InviteRequest request, builder.MapPost("api/communications/invite", async (InviteRequest request,
HttpContext context, ICommunicationsService service, CancellationToken cancellationToken = default) => HttpContext context, CommunicationsService service, CancellationToken cancellationToken = default) =>
await service.SendInviteAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var invite } => Results.Ok(invite), var result = await service.SendInviteAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Trigger an invite to an organization or candidate (specifying NotificationPlatform)") .WithDescription("Trigger an invite to an organization or candidate (specifying NotificationPlatform)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(SendInviteEndpoint).ToEndpointName()) .WithName(typeof(SendInviteEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Invite>(StatusCodes.Status200OK) .Produces<Domain.Entities.Invite>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Communications); .WithTags(EndpointTags.Communications);
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -15,23 +12,20 @@ public class CreateIndicatorEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/evidence/indicators", async (CreateIndicatorRequest request, HttpContext context, builder.MapPost("api/evidence/indicators", async (CreateIndicatorRequest request, HttpContext context,
IEvidenceService service, CancellationToken cancellationToken = default) => EvidenceService service, CancellationToken cancellationToken = default) =>
await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Define indicators and baselines/targets (M08)") .WithDescription("Define indicators and baselines/targets (M08)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateIndicatorEndpoint).ToEndpointName()) .WithName(typeof(CreateIndicatorEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence); .WithTags(EndpointTags.Evidence);
} }
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence; using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -15,22 +12,20 @@ public class CreateMilestoneEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/evidence/milestones", async (CreateMilestoneRequest request, HttpContext context, builder.MapPost("api/evidence/milestones", async (CreateMilestoneRequest request, HttpContext context,
IEvidenceService service, CancellationToken cancellationToken = default) => EvidenceService service, CancellationToken cancellationToken = default) =>
await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Define contractual milestones (M07)") .WithDescription("Define contractual milestones (M07)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateMilestoneRequest).ToEndpointName()) .WithName(typeof(CreateMilestoneRequest).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence); .WithTags(EndpointTags.Evidence);
} }
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -13,24 +11,22 @@ public sealed class GetEvidenceEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/{evidenceId}", async (string evidenceId, IEvidenceService service, builder.MapGet("api/evidence/{evidenceId}", async (string evidenceId, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetEvidenceAsync(evidenceId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence), var result = await service.GetEvidenceAsync(evidenceId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a specific evidence record and assiciated file URLs") .WithDescription("Get a specific evidence record and assiciated file URLs")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetEvidenceEndpoint).ToEndpointName()) .WithName(typeof(GetEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Evidence>(StatusCodes.Status200OK) .Produces<Domain.Models.Evidence>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence)
.CacheOutput(); .CacheOutput();
} }
@@ -1,10 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -13,19 +12,20 @@ public class GetEvidenceFilesEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/files/{page:int}/{pageSize:int}", async (IEvidenceService service, builder.MapGet("api/evidence/files/{page:int}/{pageSize:int}", async (EvidenceService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetEvidenceFilesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var evidences} => Results.Ok(evidences), var result = await service.GetEvidenceFilesAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a list of all uploaded evidence files metadata") .WithDescription("Get a list of all uploaded evidence files metadata")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetEvidenceFilesEndpoint).ToEndpointName()) .WithName(typeof(GetEvidenceFilesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK) .Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence)
.CacheOutput(); .CacheOutput();
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence;
[ApiVersionTarget(1)]
public sealed class GetEvidenceUrlEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/evidence/{evidenceId}/securelink", async (string evidenceId, IEvidenceService service,
CancellationToken cancellationToken = default) =>
await service.GetEvidenceUrlAsync(evidenceIdHash: evidenceId, cancellationToken) switch
{
{ IsSuccess: true, Value: var evidenceUrl } => Results.Ok(evidenceUrl),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Gets a secure download URL of the evidence file, URL expires (default: 15min)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetEvidenceUrlEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<string>(StatusCodes.Status200OK)
.Produces<string>(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -14,25 +12,21 @@ public sealed class GetIndicatorEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/indicators/{indicatorId}", async (string indicatorId, IEvidenceService service, builder.MapGet("api/evidence/indicators/{indicatorId}", async (string indicatorId, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetIndicatorAsync(indicatorId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var indicator } => Results.Ok(indicator), var result = await service.GetIndicatorAsync(indicatorId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a specific indicator.") .WithDescription("Get a specific indicator.")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetIndicatorEndpoint).ToEndpointName()) .WithName(typeof(GetIndicatorEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Indicator>(StatusCodes.Status200OK) .Produces<Indicator>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden) .WithTags(EndpointTags.Evidence);
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence)
.CacheOutput();
} }
} }
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -14,21 +13,21 @@ public class GetIndicatorsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/indicators/{page:int}/{pageSize:int}", async (IEvidenceService service, builder.MapGet("api/evidence/indicators/{page:int}/{pageSize:int}", async (EvidenceService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetIndicatorsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var indicators } => Results.Ok(indicators), var result = await service.GetIndicatorsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems()),
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a list of indicators") .WithDescription("Get a list of indicators")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetIndicatorsEndpoint).ToEndpointName()) .WithName(typeof(GetIndicatorsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Indicator[]>(StatusCodes.Status200OK) .Produces<Indicator[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence);
.CacheOutput();
} }
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -13,23 +11,21 @@ public class GetMilestoneEvidenceEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/milestone/{milestoneId}", async (string milestoneId, IEvidenceService service, builder.MapGet("api/evidence/milestone/{milestoneId}", async (string milestoneId, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetMilestoneEvidencesAsync(milestoneId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var evidences } => Results.Ok(evidences), var result = await service.GetMilestoneEvidencesAsync(milestoneId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone") .WithDescription("Get a list of all uploaded evidence metadata for a specific mliestone")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName()) .WithName(typeof(GetMilestoneEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK) .Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence)
.CacheOutput(); .CacheOutput();
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -13,23 +11,21 @@ public class GetProjectEvidenceEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/evidence/{projectId}", async (string projectId, IEvidenceService service, builder.MapGet("api/evidence/milestone/{projectId}", async (string projectId, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var evidence } => Results.Ok(evidence), var result = await service.GetProjectEvidencesAsync(projectId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()), return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of all uploaded evidence metadata for a specific project") .WithDescription("Get a list of all uploaded evidence metadata for a specific project")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName()) .WithName(typeof(GetProjectEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK) .Produces<Domain.Models.Evidence[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence)
.CacheOutput(); .CacheOutput();
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
@@ -14,23 +12,20 @@ public class RecordIndicatorAmountEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/evidence/indicators/{indicatorId}/actuals/{actualAmount:decimal}", async (string indicatorId, decimal actualAmount, builder.MapPost("api/evidence/indicators/{indicatorId}/actuals/{actualAmount:decimal}", async (string indicatorId, decimal actualAmount,
HttpContext context, IEvidenceService service, CancellationToken cancellationToken = default) => HttpContext context, EvidenceService service, CancellationToken cancellationToken = default) =>
await service.RecordIndicatorActualAmountAsync(indicatorId, actualAmount, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.RecordIndicatorActualAmountAsync(indicatorId, actualAmount, context.GetUserId(), cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Record actual values for indicators (M08)") .WithDescription("Record actual values for indicators (M08)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(RecordIndicatorAmountEndpoint).ToEndpointName()) .WithName(typeof(RecordIndicatorAmountEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence); .WithTags(EndpointTags.Evidence);
} }
} }
@@ -1,39 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence;
[ApiVersionTarget(1)]
public sealed class UpdateEvidenceEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/evidence", async (UpdateEvidenceRequest request, HttpContext context, IEvidenceService service,
CancellationToken cancellationToken = default) =>
await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Updates a specific evidence record metadata")
.RequireAuthorization(Constants.MainScope)
.DisableAntiforgery()
.WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Evidence);
}
}
@@ -1,19 +1,17 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Evidence; namespace PostFundManagement.Api.Endpoints.Evidence;
[ApiVersionTarget(1)] [ApiVersionTarget(1)]
public class CreateEvidenceEndpoint : IEndpoint public class UploadEvidenceFilesEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/evidence/file", async (HttpRequest request, IEvidenceService service, builder.MapPost("api/evidence/files", async (HttpRequest request, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
{ {
var userId = request.HttpContext.GetUserId(); var userId = request.HttpContext.GetUserId();
@@ -27,21 +25,19 @@ public class CreateEvidenceEndpoint : IEndpoint
if (file == null || file.Length == 0) if (file == null || file.Length == 0)
return Results.BadRequest("No file uploaded or file is empty."); return Results.BadRequest("No file uploaded or file is empty.");
return await service.CreateEvidenceAsync(userId, form, file, cancellationToken) switch var result = await service.UploadEvidenceFileAsync(userId, form, file, cancellationToken);
{
{ IsSuccess: true } => Results.Ok(), return result.IsSuccess
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), ? Results.Ok()
var r => Results.InternalServerError(r.Errors.ToProblems()) : Results.BadRequest(result.Errors.ToProblems());
};
}) })
.WithDescription("Upload supporting evidence file to S3 and register metadata (M14)") .RequireAuthorization()
.DisableAntiforgery() .DisableAntiforgery()
.RequireAuthorization(Constants.MainScope) .WithDescription("Upload supporting evidence file to S3 and register metadata (M14)")
.WithName(typeof(CreateEvidenceEndpoint).ToEndpointName()) .WithName(typeof(UploadEvidenceFilesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Evidence); .WithTags(EndpointTags.Evidence);
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution; namespace PostFundManagement.Api.Endpoints.Execution;
@@ -14,23 +12,20 @@ public class ApproveDisbursementEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/execution/disbursements/{disbursementId}/approve", async (string disbursementId, builder.MapPost("api/execution/disbursements/{disbursementId}/approve", async (string disbursementId,
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) => ExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
await service.ApproveDisbursementAsync(disbursementId, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.ApproveDisbursementAsync(disbursementId, context.GetUserId(), cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()), ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Approve disbursement and release payment (M06)") .WithDescription("Approve disbursement and release payment (M06)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(ApproveDisbursementEndpoint).ToEndpointName()) .WithName(typeof(ApproveDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution); .WithTags(EndpointTags.Execution);
} }
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution; using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution; namespace PostFundManagement.Api.Endpoints.Execution;
@@ -15,23 +12,20 @@ public class CreateDisbursementEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/execution/disbursements", async (CreateDisbursementRequest request, builder.MapPost("api/execution/disbursements", async (CreateDisbursementRequest request,
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) => ExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()), ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Create a new disbursement schedule (M06)") .WithDescription("Create a new disbursement schedule (M06)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateDisbursementEndpoint).ToEndpointName()) .WithName(typeof(CreateDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution); .WithTags(EndpointTags.Execution);
} }
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution; using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution; namespace PostFundManagement.Api.Endpoints.Execution;
@@ -14,24 +11,21 @@ public class CreateRiskEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/execution/risks", async (CreateRiskRequest request, IExecutionService service, builder.MapPost("api/execution/risks", async (CreateRiskRequest request, ExecutionService service,
HttpContext context, CancellationToken cancellationToken = default) => HttpContext context, CancellationToken cancellationToken = default) =>
await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Add risk to risk register (M09)") .WithDescription("Add risk to risk register (M09)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateRiskEndpoint).ToEndpointName()) .WithName(typeof(CreateRiskEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution); .WithTags(EndpointTags.Execution);
} }
} }
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution;
[ApiVersionTarget(1)]
public sealed class GetDisbursementEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/execution/disbursements/{disbursementId}", async (string disbursementId, IExecutionService service,
CancellationToken cancellationToken = default) =>
await service.GetDisbursementAsync(disbursementIdHash: disbursementId, cancellationToken) switch
{
{ IsSuccess: true, Value: var disbursement } => Results.Ok(disbursement),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a specific disbursement")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Disbursement>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution; namespace PostFundManagement.Api.Endpoints.Execution;
@@ -14,22 +13,21 @@ public class GetDisbursementsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/execution/disbursements", async (IExecutionService service, builder.MapGet("api/execution/disbursements", async (ExecutionService service,
int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) =>
await service.GetDisbursementsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var disbursements } => Results.Ok(disbursements), var result = await service.GetDisbursementsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of disbursements") .WithDescription("Get a list of disbursements")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetDisbursementsEndpoint).ToEndpointName()) .WithName(typeof(GetDisbursementsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Disbursement[]>(StatusCodes.Status200OK) .Produces<Disbursement[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Execution) .WithTags(EndpointTags.Execution);
.CacheOutput();
} }
} }
@@ -0,0 +1,32 @@
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
namespace PostFundManagement.Api.Endpoints.Execution;
[ApiVersionTarget(1)]
public class GetProjectRisksEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/execution/risks/project/{projectId}", async (string projectId,
ExecutionService service, CancellationToken cancellationToken = default) =>
{
var result = await service.GetProjectRisksAsync(projectId, cancellationToken);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
})
.WithDescription("Get a list of risks for a project")
.WithName(typeof(GetProjectRisksEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Risk[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Execution);
}
}
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution;
[ApiVersionTarget(1)]
public sealed class GetRiskEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/execution/risks/{riskId}", async (string riskId, IExecutionService service,
CancellationToken cancellationToken = default) =>
await service.GetRiskAsync(riskIdHash: riskId, cancellationToken) switch
{
{ IsSuccess: true, Value: var risk } => Results.Ok(risk),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a specific risk")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetRiskEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Risk>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution)
.CacheOutput();
}
}
@@ -1,10 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution; namespace PostFundManagement.Api.Endpoints.Execution;
@@ -13,21 +12,20 @@ public class GetRisksEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/execution/risks", async (IExecutionService service, builder.MapGet("api/execution/risks", async (ExecutionService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetRisksAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var risks } => Results.Ok(risks), var result = await service.GetRisksAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of all risks") .WithDescription("Get a list of all risks")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetRisksEndpoint).ToEndpointName()) .WithName(typeof(GetRisksEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution);
.WithTags(EndpointTags.Execution)
.CacheOutput();
} }
} }
@@ -1,35 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution;
[ApiVersionTarget(1)]
public sealed class UpdateDisbursementEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/execution/disbursements", async (UpdateDisbursementRequest request, HttpContext context,
IExecutionService service, CancellationToken cancellationToken = default) =>
await service.UpdateDisbursementAsync(request, userId: context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Update the details of a specific disbursement")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Execution);
}
}
@@ -1,35 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Execution;
[ApiVersionTarget(1)]
public sealed class UpdateRiskEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/execution/risks", async (UpdateRiskRequest request,
IExecutionService service, CancellationToken cancellationToken = default) =>
await service.UpdateRiskAsync(request, cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Update the details of a specific risk")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateRiskEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution);
}
}
@@ -1,34 +1,30 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance; using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)] [ApiVersionTarget(1)]
public sealed class CreateOrganisationEndpoint : IEndpoint public class CreateOrganisationEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/governance/organisations", async (CreateOrganisationRequest request, HttpContext context, builder.MapPost("api/governance/organisations", async (CreateOrganisationRequest request, HttpContext context,
IGovernanceService service, CancellationToken cancellationToken = default) => GovernanceService service, CancellationToken cancellationToken = default) =>
await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Create a new organization profile") .WithDescription("Create a new organization profile")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateOrganisationEndpoint).ToEndpointName()) .WithName(typeof(CreateOrganisationEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Governance); .WithTags(EndpointTags.Governance);
} }
@@ -1,39 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public sealed class CreatePortfolioEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPost("api/governance/organisations/portfolio", async (CreatePortfolioRequest request, HttpContext context,
IGovernanceService service, CancellationToken cancellationToken = default) =>
await service.CreatePortfolioAsync(request, context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Create a new organization portfolio")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreatePortfolioEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Governance);
}
}
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance; using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
@@ -13,21 +11,21 @@ public class DashboardEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/dashboard", async (IGovernanceService service, builder.MapGet("api/governance/dashboard", async (GovernanceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetDashboardAsync(cancellationToken) switch
{ {
{ IsSuccess: true, Value: var dashboard } => Results.Ok(dashboard), var result = await service.GetDashboardAsync(cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get executive portfolio dashboard metrics and KPIs") .WithDescription("Get executive portfolio dashboard metrics and KPIs")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(DashboardEndpoint).ToEndpointName()) .WithName(typeof(DashboardEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Dashboard>(StatusCodes.Status200OK) .Produces<Dashboard>(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance);
.WithTags(EndpointTags.Governance)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
@@ -14,23 +12,21 @@ public class GetInstrumentsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/instruments/{programmeId}", async (string programmeId, IGovernanceService service, builder.MapGet("api/governance/instruments", async (GovernanceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetInstrumentsAsync(programmeId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs), var result = await service.GetInstrumentsAsync(cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()), return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get all funding instruments") .WithDescription("Get all funding instruments")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetInstrumentsEndpoint).ToEndpointName()) .WithName(typeof(GetInstrumentsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Instrument[]>(StatusCodes.Status200OK) .Produces<Instrument[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance);
.WithTags(EndpointTags.Governance)
.CacheOutput();
} }
} }
@@ -0,0 +1,32 @@
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public class GetOrganisationByIdEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/governance/organisations/{organisationId}", async (string organisationId,
GovernanceService service, CancellationToken cancellationToken = default) =>
{
var result = await service.GetOrganisationByIdAsync(organisationId, cancellationToken);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
})
.WithDescription("Get organization profile by ID")
.WithName(typeof(GetOrganisationByIdEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Organisation>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Governance);
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public class GetOrganisationEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/governance/organisations/{organisationId}", async (string organisationId,
IGovernanceService service, CancellationToken cancellationToken = default) =>
await service.GetOrganisationAsync(organisationId, cancellationToken) switch
{
{ IsSuccess: true, Value: var organisation } => Results.Ok(organisation),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get organization profile by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetOrganisationEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Organisation>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
@@ -14,21 +13,21 @@ public class GetOrganisationsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/organisations", async (IGovernanceService service, builder.MapGet("api/governance/organisations", async (GovernanceService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetOrganisationsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var orgnanisations } => Results.Ok(orgnanisations), var result = await service.GetOrganisationsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a paginated directory of organizations") .WithDescription("Get a paginated directory of organizations")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetOrganisationsEndpoint).ToEndpointName()) .WithName(typeof(GetOrganisationsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Organisation[]>(StatusCodes.Status200OK) .Produces<Organisation[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Governance) .WithTags(EndpointTags.Governance);
.CacheOutput();
} }
} }
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public sealed class GetPortfolioEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/governance/portfolios/{portfolioId}", async (string portfolioId, IGovernanceService service,
CancellationToken cancellationToken = default) =>
await service.GetPortfolioAsync(portfolioIdHash: portfolioId, cancellationToken) switch
{
{ IsSuccess: true, Value: var portfolio } => Results.Ok(portfolio),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a specific portfolios")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetPortfolioEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Portfolio[]>(StatusCodes.Status200OK)
.Produces<Portfolio[]>(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance)
.CacheOutput();
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public sealed class GetPortfolioProgrammesEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/governance/portfolios/{portfolioId}/programmes", async (string portfolioId, IGovernanceService service,
CancellationToken cancellationToken = default) =>
await service.GetPortfolioProgrammesAsync(portfolioId, cancellationToken) switch
{
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a programme's portfolios")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetPortfolioProgrammesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Programme[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance)
.CacheOutput();
}
}
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
@@ -14,23 +12,21 @@ public class GetPortfoliosEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/organisations/{organisationId}/portfolio", async (string organisationId, builder.MapGet("api/governance/portfolios", async (GovernanceService service,
IGovernanceService service, CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetPorfoliosAsync(organisationId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios), var result = await service.GetPorfoliosAsync(cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get all portfolios") .WithDescription("Get all portfolios")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetPortfoliosEndpoint).ToEndpointName()) .WithName(typeof(GetPortfoliosEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Portfolio[]>(StatusCodes.Status200OK) .Produces<Portfolio[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance);
.WithTags(EndpointTags.Governance)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance; namespace PostFundManagement.Api.Endpoints.Governance;
@@ -14,23 +12,21 @@ public class GetProgrammesEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/programmes/{portfolioId}", async (string portfolioId, IGovernanceService service, builder.MapGet("api/governance/programmes", async (GovernanceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetProgrammesAsync(portfolioId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes), var result = await service.GetProgrammesAsync(cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get all programmes") .WithDescription("Get all programmes")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName()) .WithName(typeof(GetProgrammesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Programme[]>(StatusCodes.Status200OK) .Produces<Programme[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance);
.WithTags(EndpointTags.Governance)
.CacheOutput();
} }
} }
@@ -1,36 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public sealed class UpdateOrganisationEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
IGovernanceService service, CancellationToken cancellationToken = default) =>
await service.UpdateOrganisationAsync(request, cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Update an existing organization profile")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Governance);
}
}
@@ -1,39 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Governance;
[ApiVersionTarget(1)]
public sealed class UpdatePortfolioEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/governance/organisations/portfolio", async (UpdatePortfolioRequest request, HttpContext context,
IGovernanceService service, CancellationToken cancellationToken = default) =>
await service.UpdatePortfolioAsync(request, context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Update an existing organization portfolio")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdatePortfolioEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance);
}
}
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants; using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -15,24 +12,20 @@ public class CreateAwardEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/grants/awards", async (CreateAwardRequest request, HttpContext context, builder.MapPost("api/grants/awards", async (CreateAwardRequest request, HttpContext context,
IGrantsService service, CancellationToken cancellationToken = default) => GrantsService service, CancellationToken cancellationToken = default) =>
await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), ? Results.Ok()
var r => Results.InternalServerError(r.Errors.ToProblems()) : Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Register an approved funding award (M01)") .WithDescription("Register an approved funding award (M01)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateAwardEndpoint).ToEndpointName()) .WithName(typeof(CreateAwardEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants); .WithTags(EndpointTags.Grants);
} }
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants; using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -15,24 +12,19 @@ public class CreateBudgetEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/grants/budgets", async (CreateBudgetRequest request, HttpContext context, builder.MapPost("api/grants/budgets", async (CreateBudgetRequest request, HttpContext context,
IGrantsService service, CancellationToken cancellationToken = default) => GrantsService service, CancellationToken cancellationToken = default) =>
await service.CreateBudgetAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateBudgetAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r when r.HasError<CreateError>() => Results.BadRequest(), ? Results.Ok()
var r => Results.InternalServerError(r.Errors.ToProblems()) : Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Create a new budget line (M05)") .WithDescription("Create a new budget line (M05)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateBudgetEndpoint).ToEndpointName()) .WithName(typeof(CreateBudgetEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants); .WithTags(EndpointTags.Grants);
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants; using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -15,21 +12,19 @@ public class CreateContractEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/grants/contracts", async (CreateContractRequest request, HttpContext context, builder.MapPost("api/grants/contracts", async (CreateContractRequest request, HttpContext context,
IGrantsService service, CancellationToken cancellationToken = default) => GrantsService service, CancellationToken cancellationToken = default) =>
await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.CreateContractAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Create a contract record from an approved award (M02)") .WithDescription("Create a contract record from an approved award (M02)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateContractEndpoint).ToEndpointName()) .WithName(typeof(CreateContractEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants); .WithTags(EndpointTags.Grants);
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants; using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -15,20 +12,19 @@ public class ExecuteContractEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/grants/contracts/execute", async (ExecuteContractRequest request, HttpContext context, builder.MapPost("api/grants/contracts/execute", async (ExecuteContractRequest request, HttpContext context,
IGrantsService service, CancellationToken cancellationToken = default) => GrantsService service, CancellationToken cancellationToken = default) =>
await service.ExecuteContractAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.ExecuteContractAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Execute the contract and lock the contractual version") .WithDescription("Execute the contract and lock the contractual version")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(ExecuteContractEndpoint).ToEndpointName()) .WithName(typeof(ExecuteContractEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants); .WithTags(EndpointTags.Grants);
} }
@@ -0,0 +1,32 @@
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetAwardByIdEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/awards/{awardId}", async (string awardId, GrantsService service,
CancellationToken cancellationToken = default) =>
{
var result = await service.GetAwardByIdAsync(awardId, cancellationToken);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
})
.WithDescription("Get award by ID")
.WithName(typeof(GetAwardByIdEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Award>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants);
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetAwardEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/awards/{awardId}", async (string awardId, IGrantsService service,
CancellationToken cancellationToken = default) =>
await service.GetAwardAsync(awardId, cancellationToken) switch
{
{ IsSuccess: true, Value: var award } => Results.Ok(award),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get award by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetAwardEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Award>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -14,21 +13,21 @@ public class GetAwardsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/awards", async (IGrantsService service, builder.MapGet("api/grants/awards", async (GrantsService service,
int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 10, CancellationToken cancellationToken = default) =>
await service.GetAwardsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var awards } => Results.Ok(awards), var result = await service.GetAwardsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a list of awards") .WithDescription("Get a list of awards")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetAwardsEndpoint).ToEndpointName()) .WithName(typeof(GetAwardsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Award[]>(StatusCodes.Status200OK) .Produces<Award[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants) .WithTags(EndpointTags.Grants);
.CacheOutput();
} }
} }
@@ -0,0 +1,32 @@
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetBudgetByIdEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/budgets/{budgetId}", async (string budgetId, GrantsService service,
CancellationToken cancellationToken = default) =>
{
var result = await service.GetBudgetByIdAsync(budgetId, cancellationToken);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
})
.WithDescription("Get budget by ID")
.WithName(typeof(GetBudgetByIdEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Budget>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants);
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetBudgetEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/budgets/{budgetId}", async (string budgetId, IGrantsService service,
CancellationToken cancellationToken = default) =>
await service.GetBudgetAsync(budgetId, cancellationToken) switch
{
{ IsSuccess: true, Value: var budget } => Results.Ok(budget),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get budget by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetBudgetEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Budget>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -14,21 +13,21 @@ public class GetBudgetsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/budgets", async (IGrantsService service, builder.MapGet("api/grants/budgets", async (GrantsService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetBudgetsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var budgets } => Results.Ok(budgets), var result = await service.GetBudgetsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get all budgets") .WithDescription("Get all budgets")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetBudgetsEndpoint).ToEndpointName()) .WithName(typeof(GetBudgetsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Budget[]>(StatusCodes.Status200OK) .Produces<Budget[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants) .WithTags(EndpointTags.Grants);
.CacheOutput();
} }
} }
@@ -0,0 +1,32 @@
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetContractByIdEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/contracts/{contractId}", async (string contractId, GrantsService service,
CancellationToken cancellationToken = default) =>
{
var result = await service.GetContractByIdAsync(contractId, cancellationToken);
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
})
.WithDescription("Get contract by ID")
.WithName(typeof(GetContractByIdEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Contract>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants);
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public class GetContractEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/contracts/{contractId}", async (string contractId, IGrantsService service,
CancellationToken cancellationToken = default) =>
await service.GetContractAsync(contractId, cancellationToken) switch
{
{ IsSuccess: true, Value: var contract } => Results.Ok(contract),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get contract by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetContractEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Contract>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public sealed class GetContractUrlEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/grants/contracts/{contractId}/securelink", async (string contractId, IGrantsService service,
CancellationToken cancellationToken = default) =>
await service.GetContractUrlAsync(contractId, cancellationToken) switch
{
{ IsSuccess: true, Value: var contractUrl} => Results.Ok(contractUrl),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Gets a secure download URL, has expiry period (default 15min)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetContractUrlEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<string>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants; namespace PostFundManagement.Api.Endpoints.Grants;
@@ -14,21 +13,21 @@ public class GetContractsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/contracts", async (IGrantsService service, builder.MapGet("api/grants/contracts", async (GrantsService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetContractsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var contracts } => Results.Ok(contracts), var result = await service.GetContractsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get all contracts") .WithDescription("Get all contracts")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetContractsEndpoint).ToEndpointName()) .WithName(typeof(GetContractsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Contract[]>(StatusCodes.Status200OK) .Produces<Contract[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Grants) .WithTags(EndpointTags.Grants);
.CacheOutput();
} }
} }
@@ -1,39 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public sealed class UpdateContractEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/grants/contracts", async (UpdateContractRequest request, IGrantsService service,
CancellationToken cancellationToken = default) =>
await service.UpdateContractAsync(request, cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Updates a specific contract")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateContractEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,48 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Grants;
[ApiVersionTarget(1)]
public sealed class UploadContractEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPost("api/grants/contracts/{contractId}", async (string contractId, HttpRequest request, IGrantsService service,
CancellationToken cancellationToken = default) =>
{
if (!request.HasFormContentType)
return Results.BadRequest("Request must be a multipart form.");
var form = await request.ReadFormAsync(cancellationToken);
var file = form.Files.GetFile("file");
if (file == null || file.Length == 0)
return Results.BadRequest("No file uploaded or file is empty.");
return await service.UploadContractAsync(contractId, request.HttpContext.GetUserId(), file, cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
};
})
.WithDescription("Uploads a contract file securely")
.RequireAuthorization(Constants.MainScope)
.DisableAntiforgery()
.WithName(typeof(UploadContractEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Grants)
.CacheOutput();
}
}
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -15,21 +12,19 @@ public class CreateActivityEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/projects/activities", async (CreateActivityRequest request, builder.MapPost("api/projects/activities", async (CreateActivityRequest request,
HttpContext context, IProjectService service, CancellationToken cancellationToken = default) => HttpContext context, ProjectService service, CancellationToken cancellationToken = default) =>
await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } result => Results.Ok(), var result = await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.RequireAuthorization(Constants.MainScope)
.WithDescription("Add a new activity under project (M04)") .WithDescription("Add a new activity under project (M04)")
.WithName(typeof(CreateActivityEndpoint).ToEndpointName()) .WithName(typeof(CreateActivityEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects); .WithTags(EndpointTags.Projects);
} }
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public sealed class CreateProgrammeEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPost("api/projects/programme", async (CreateProgrammeRequest request, HttpContext context,
IProjectService service, CancellationToken cancellationToken = default) =>
await service.CreateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
var r when r.HasError<CreateError>() => Results.BadRequest(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Create programme")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateProgrammeEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects);
}
}
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -15,23 +12,19 @@ public class CreateProjectEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/projects", async (CreateProjectRequest request, HttpContext context, builder.MapPost("api/projects", async (CreateProjectRequest request, HttpContext context,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } result => Results.Ok(), var result = await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), ? Results.Ok()
var r => Results.InternalServerError(r.Errors.ToProblems()) : Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Create a new project (M04)") .WithDescription("Create a new project (M04)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(CreateProjectEndpoint).ToEndpointName()) .WithName(typeof(CreateProjectEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects); .WithTags(EndpointTags.Projects);
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,22 +12,19 @@ public class GetActivitiesEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/{projectId}/activities", async (string projectId, builder.MapGet("api/projects/{projectId}/activities", async (string projectId,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.GetActivitiesAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var activities } => Results.Ok(activities), var result = await service.GetActivitiesAsync(projectId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of activities for a project") .WithDescription("Get a list of activities for a project")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetActivitiesEndpoint).ToEndpointName()) .WithName(typeof(GetActivitiesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Activity[]>(StatusCodes.Status200OK) .Produces<Domain.Models.Activity[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects)
.CacheOutput();
} }
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,24 +12,20 @@ public class GetActivityEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/activities/{activityId}", async (string activityId, builder.MapGet("api/projects/activities/{activityId}", async (string activityId,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.GetActivityAsync(activityId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var activity } => Results.Ok(activity), var result = await service.GetActivityAsync(activityId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Gets a specific activity by it's id") .WithDescription("Gets a specific activity by it's id")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetActivityEndpoint).ToEndpointName()) .WithName(typeof(GetActivityEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Domain.Models.Activity>(StatusCodes.Status200OK) .Produces<Domain.Models.Activity>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,24 +12,21 @@ public class GetMilestoneEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/milestones/{milestoneId}", async (string milestoneId, IProjectService service, builder.MapGet("api/projects/milestones/{milestoneId}", async (string milestoneId, ProjectService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetMilestoneAsync(milestoneId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var milestone } => Results.Ok(milestone), var result = await service.GetMilestoneAsync(milestoneId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Gets a specific milestone") .WithDescription("Gets a specific milestone")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetMilestoneEndpoint).ToEndpointName()) .WithName(typeof(GetMilestoneEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Milestone>(StatusCodes.Status200OK) .Produces<Milestone>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects)
.CacheOutput(); .CacheOutput();
} }
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,21 +13,22 @@ public class GetMilestonesEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/milestones/{page:int}/{pageSize:int}", async (IProjectService service, builder.MapGet("api/projects/milestones/{page:int}/{pageSize:int}", async (ProjectService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetMilestonesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones), var result = await service.GetMilestonesAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.RequireAuthorization()
.WithDescription("Get a list of all milestones") .WithDescription("Get a list of all milestones")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetMilestonesEndpoint).ToEndpointName()) .WithName(typeof(GetMilestonesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Milestone[]>(StatusCodes.Status200OK) .Produces<Milestone[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public sealed class GetProgrammeEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/projects/programme/{programmeId}", async (string programmeId, IProjectService service,
CancellationToken cancellationToken = default) =>
await service.GetProgrammeAsync(programmeId, cancellationToken) switch
{
{ IsSuccess: true, Value: var programme } => Results.Ok(programme),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get specific programme")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProgrammeEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Programme>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects)
.CacheOutput();
}
}
@@ -1,38 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public sealed class GetProgrammeProjectsEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/projects/{programmeId}/programme", async (string programmeId,
IProjectService service, CancellationToken cancellationToken = default) =>
await service.GetProgrammeProjectsAsync(programmeId, cancellationToken) switch
{
{ IsSuccess: true, Value: var projects } => Results.Ok(projects),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()),
})
.WithDescription("Get a list of all projects under a programme")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProgrammeProjectsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Project[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects)
.CacheOutput();
}
}
@@ -1,36 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public sealed class GetProgrammesEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/projects/programme/{page:int}/{pageSize:int}", async (IProjectService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetProgrammesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a list of all programmes")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName("Projects"))
.MapToApiVersion(new ApiVersion(1))
.Produces<Programme[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects)
.CacheOutput();
}
}
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,25 +12,21 @@ public class GetProjectEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/{projectId}", async (string projectId, IProjectService service, builder.MapGet("api/projects/{projectId}", async (string projectId, ProjectService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetProjectAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var project } => Results.Ok(project), var result = await service.GetProjectAsync(projectId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get project by ID") .WithDescription("Get project by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectEndpoint).ToEndpointName()) .WithName(typeof(GetProjectEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Project[]>(StatusCodes.Status200OK) .Produces<Project[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,24 +12,21 @@ public class GetProjectIndicatorsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/indicators/project/{projectId}", async (string projectId, IProjectService service, builder.MapGet("api/projects/indicators/project/{projectId}", async (string projectId, ProjectService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var indicators } => Results.Ok(indicators), var result = await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get get a detailed list of project indicators") .WithDescription("Get get a detailed list of project indicators")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectIndicatorsEndpoint).ToEndpointName()) .WithName(typeof(GetProjectIndicatorsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Indicator[]>(StatusCodes.Status200OK) .Produces<Indicator[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -15,23 +13,20 @@ public class GetProjectMilestonesEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/{projectId}/milestones", async (string projectId, builder.MapGet("api/projects/{projectId}/milestones", async (string projectId,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.GetProjectMilestonesAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones), var result = await service.GetProjectMilestonesAsync(projectId, cancellationToken);
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Get a list of milestones for a project") .WithDescription("Get a list of milestones for a project")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectMilestonesEndpoint).ToEndpointName()) .WithName(typeof(GetProjectMilestonesEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Milestone[]>(StatusCodes.Status200OK) .Produces<Milestone[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects)
.CacheOutput();
} }
} }
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public class GetProjectRisksEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/projects/{projectId}/risks", async (string projectId,
IProjectService service, CancellationToken cancellationToken = default) =>
await service.GetProjectRisksAsync(projectId, cancellationToken) switch
{
{ IsSuccess: true, Value: var risks } => Results.Ok(risks),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Get a list of risks for a project")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectRisksEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<Risk[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects)
.CacheOutput();
}
}
@@ -1,11 +1,10 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -14,21 +13,21 @@ public class GetProjectsEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects", async (IProjectService service, builder.MapGet("api/projects", async (ProjectService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetProjectsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var projects } => Results.Ok(projects), var result = await service.GetProjectsAsync(Pagination.Create(page, pageSize), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Get a list of projects") .WithDescription("Get a list of projects")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectsEndpoint).ToEndpointName()) .WithName(typeof(GetProjectsEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Project[]>(StatusCodes.Status200OK) .Produces<Project[]>(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,10 +1,8 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -13,19 +11,20 @@ public sealed class UpdateMilestoneEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/projects/milestone", async (UpdateMilestoneRequest request, IProjectService service, builder.MapPut("api/projects/milestone", async (UpdateMilestoneRequest request, ProjectService service,
HttpContext context, CancellationToken cancellationToken = default) => HttpContext context, CancellationToken cancellationToken = default) =>
await service.UpdateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.UpdateMilestoneAsync(request, context.GetUserId(), cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Update project milestone details") .WithDescription("Update project milestone details")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateMilestoneEndpoint).ToEndpointName()) .WithName(typeof(UpdateMilestoneEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects); .WithTags(EndpointTags.Projects);
} }
@@ -1,37 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects;
[ApiVersionTarget(1)]
public sealed class UpdateProgrammeEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapPut("api/projects/programme", async (UpdateProgrammeRequest request, HttpContext context,
IProjectService service, CancellationToken cancellationToken = default) =>
await service.UpdateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch
{
{ IsSuccess: true } => Results.Ok(),
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Update existing programme")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateProgrammeEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects);
}
}
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -15,20 +12,19 @@ public sealed class UpdateProjectEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/projects", async (UpdateProjectRequest request, builder.MapPut("api/projects", async (UpdateProjectRequest request,
IProjectService service, HttpContext context, CancellationToken cancellationToken = default) => ProjectService service, HttpContext context, CancellationToken cancellationToken = default) =>
await service.UpdateProjectAsync(request, userId: context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.UpdateProjectAsync(request, userId: context.GetUserId(), cancellationToken);
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Update project details") .WithDescription("Update project details")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateProjectEndpoint).ToEndpointName()) .WithName(typeof(UpdateProjectEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects); .WithTags(EndpointTags.Projects);
} }
@@ -1,11 +1,8 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Projects; using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Projects; namespace PostFundManagement.Api.Endpoints.Projects;
@@ -15,22 +12,20 @@ public class UpdateProjectStatusEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/projects/status", async (UpdateProjectStatusRequest request, HttpContext context, builder.MapPut("api/projects/status", async (UpdateProjectStatusRequest request, HttpContext context,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()), return result.IsSuccess
var r => Results.InternalServerError(r.Errors.ToProblems()) ? Results.Ok()
: Results.BadRequest(result.Errors.ToProblems());
}) })
.WithDescription("Update project status (M04)") .WithDescription("Update project status (M04)")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateProjectStatusEndpoint).ToEndpointName()) .WithName(typeof(UpdateProjectStatusEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces<Microsoft.AspNetCore.Mvc.ProblemDetails>(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects); .WithTags(EndpointTags.Projects);
} }
} }
@@ -1,36 +0,0 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Tools;
[ApiVersionTarget(1)]
public sealed class EncodeIdEndpoint : IEndpoint
{
public void Map(IEndpointRouteBuilder builder)
{
builder.MapGet("api/tools/hashid/{id64:long}", async (long id64, IToolService service,
CancellationToken cancellationToken) =>
await service.EncodeLong(id64, cancellationToken) switch
{
{ IsSuccess: true, Value: var hash } => Results.Ok(hash),
var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
})
.WithDescription("Hashes an int64 identifier")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(EncodeIdEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1))
.Produces<string>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Tools)
.CacheOutput();
}
}
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Users; namespace PostFundManagement.Api.Endpoints.Users;
@@ -14,23 +12,21 @@ public sealed class GetUserEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/users/{userId:Guid}", async (Guid userId, IUserService service, builder.MapGet("api/users/{userId:Guid}", async (Guid userId, UserService service,
HttpContext context, CancellationToken cancellationToken = default) => HttpContext context, CancellationToken cancellationToken = default) =>
await service.GetUserAsync(userId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var user } => Results.Ok(user), var result = await service.GetUserAsync(userId, cancellationToken);
var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Gets the metadata of a single user.") .WithDescription("Gets the metadata of a single user.")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetUserEndpoint).ToEndpointName()) .WithName(typeof(GetUserEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<User>(StatusCodes.Status200OK) .Produces<User>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Users);
.WithTags(EndpointTags.Users)
.CacheOutput();
} }
} }
@@ -1,4 +1,3 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Users; using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain; using PostFundManagement.Domain;
@@ -6,7 +5,6 @@ using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Models; using PostFundManagement.Domain.Models;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Users; namespace PostFundManagement.Api.Endpoints.Users;
@@ -15,22 +13,21 @@ public class GetUsersEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/users/{page:int}/{pageSize:int}", async (UserFilterRequest request, IUserService service, builder.MapPost("api/users/{page:int}/{pageSize:int}", async (UserFilterRequest request, UserService service,
int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) => int page = 1, int pageSize = 100, CancellationToken cancellationToken = default) =>
await service.GetUsersAsync(Pagination.Create(page, pageSize), request, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var users } => Results.Ok(users), var result = await service.GetUsersAsync(Pagination.Create(page, pageSize), request, cancellationToken);
var r => Results.InternalServerError(r.Errors.ToProblems())
return result.IsSuccess
? Results.Ok(result.Value)
: Results.NotFound();
}) })
.WithDescription("Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting") .WithDescription("Paginated, searchable directory of system users. Supports filtering by ActivityStatus, searching by Email, and sorting")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetUsersEndpoint).ToEndpointName()) .WithName(typeof(GetUsersEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<User[]>(StatusCodes.Status200OK) .Produces<User[]>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Users);
.WithTags(EndpointTags.Users)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Communications.Events; using PostFundManagement.Application.Services.Communications.Events;
using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Users; namespace PostFundManagement.Api.Endpoints.Users;
@@ -15,28 +13,22 @@ public class SyncUserEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPost("api/integration/sync", async (IJobOrchestrator jobOrchestrator, builder.MapPost("api/integration/sync", async (IJobOrchestrator jobOrchestrator,
HttpContext context, IUserService service, CancellationToken cancellationToken = default) => HttpContext context, UserService service, CancellationToken cancellationToken = default) =>
{ {
var result = await service.SyncUserAsync(context.GetUserId(), context.GetUserEmail(), cancellationToken); var result = await service.SyncUserAsync(context.GetUserId(), context.GetUserEmail(), cancellationToken);
if (result.IsSuccess) if (result.IsSuccess)
await jobOrchestrator.SendAsync(SyncUserEvent.Create(), cancellationToken); await jobOrchestrator.SendAsync(SyncUserEvent.Create(), cancellationToken);
return result switch return result.IsSuccess
{ ? Results.Ok()
{ IsSuccess: true } => Results.Ok(), : Results.BadRequest(result.Errors.ToProblems());
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems())
};
}) })
.WithDescription("Trigger integration and data synchronization in the background") .WithDescription("Trigger integration and data synchronization in the background")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(SyncUserEndpoint).ToEndpointName()) .WithName(typeof(SyncUserEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Users);
.WithTags(EndpointTags.Users)
.CacheOutput();
} }
} }
@@ -1,11 +1,9 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain; using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using Problems = Microsoft.AspNetCore.Mvc.ProblemDetails;
namespace PostFundManagement.Api.Endpoints.Users; namespace PostFundManagement.Api.Endpoints.Users;
@@ -15,22 +13,20 @@ public sealed class UpdateUserStatusEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/users/{userId:Guid}/status/{status:int}", async (Guid userId, ActivityStatus status, builder.MapGet("api/users/{userId:Guid}/status/{status:int}", async (Guid userId, ActivityStatus status,
IUserService service, CancellationToken cancellationToken = default) => UserService service, CancellationToken cancellationToken = default) =>
await service.UpdateUserStatusAsync(userId, status, cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), var result = await service.UpdateUserStatusAsync(userId, status, cancellationToken);
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) return result.IsSuccess
? Results.Ok()
: Results.NotFound();
}) })
.WithDescription("Changes the Activity status of the user in the system") .WithDescription("Changes the Activity status of the user in the system")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateUserStatusEndpoint).ToEndpointName()) .WithName(typeof(UpdateUserStatusEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Users); .WithTags(EndpointTags.Users);
} }
} }
@@ -10,14 +10,6 @@
<PackageLicenseFile>..\LICENSE</PackageLicenseFile> <PackageLicenseFile>..\LICENSE</PackageLicenseFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;AV0029;AV0030;</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;AV0029;AV0030;</NoWarn>
</PropertyGroup>
<!-- Security (IODC)--> <!-- Security (IODC)-->
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" /> <PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
@@ -25,8 +17,8 @@
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityModel" Version="6.2.0" /> <PackageReference Include="IdentityModel" Version="6.2.0" />
<PackageReference Include="KubernetesClient" Version="19.0.2" /> <PackageReference Include="KubernetesClient" Version="19.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
<Using Include="Microsoft.AspNetCore.Authentication" /> <Using Include="Microsoft.AspNetCore.Authentication" />
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" /> <Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
@@ -51,7 +43,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" /> <PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" />
<PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" /> <PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" />
<PackageReference Include="Asp.Versioning.Http" Version="10.2.3" /> <PackageReference Include="Asp.Versioning.Http" Version="10.2.2" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" /> <PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
<Using Include="Asp.Versioning" /> <Using Include="Asp.Versioning" />
@@ -60,8 +52,8 @@
<!-- API Documentation --> <!-- API Documentation -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
<PackageReference Include="Scalar.AspNetCore" Version="2.17.4" /> <PackageReference Include="Scalar.AspNetCore" Version="2.17.1" />
<Using Include="Scalar.AspNetCore" /> <Using Include="Scalar.AspNetCore" />
<Using Include="Microsoft.OpenApi" /> <Using Include="Microsoft.OpenApi" />
+11 -113
View File
@@ -1,38 +1,20 @@
using Microsoft.FeatureManagement;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Filters; using PostFundManagement.Application.Filters;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Health; using PostFundManagement.Domain.Health;
using PostFundManagement.Domain.Mediator; using PostFundManagement.Domain.Mediator;
using static PostFundManagement.Domain.Extensions.Constants; using static PostFundManagement.Domain.Extensions.Constants;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFeatureManagement(); builder.Services.AddEndpointsApiExplorer()
.AddApiVersioning(options => options.ApiVersionReader = new HeaderApiVersionReader());
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("version")
);
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddApiVersioning().AddApiExplorer();
builder.Services.AddScoped<ActiveUserFilter>(); builder.Services.AddScoped<ActiveUserFilter>();
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly()); builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
builder.Services.AddApiServices(builder.Configuration); builder.Services.AddApiServices(builder.Configuration);
builder.Services.AddSharedServices();
builder.Services.AddRegisterServicesWithCaching(builder.Configuration);
builder.Services.AddEndpointAndServiceCaching(builder.Configuration);
builder.Services.AddMediator(); builder.Services.AddMediator();
builder.Services.AddSecurityApiSdk(builder.Configuration); builder.Services.AddSecurityApiSdk(builder.Configuration);
@@ -45,6 +27,7 @@ builder.Services.AddQuartzScheduler(DefaultSchedulerName, builder.Configuration)
builder.Services.AddEmailServices(builder.Configuration); builder.Services.AddEmailServices(builder.Configuration);
builder.Services.AddHashServices(builder.Configuration); builder.Services.AddHashServices(builder.Configuration);
builder.Services.AddGarageS3(builder.Configuration); builder.Services.AddGarageS3(builder.Configuration);
builder.Services.AddRegisterServices();
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddDataProtectionDatabase(builder.Configuration); builder.Services.AddDataProtectionDatabase(builder.Configuration);
@@ -55,89 +38,24 @@ builder.Services.AddHealthChecks()
.AddCheck<PostgresHealthCheck>("PostgresDatabase") .AddCheck<PostgresHealthCheck>("PostgresDatabase")
.AddCheck("Self", () => HealthCheckResult.Healthy()); .AddCheck("Self", () => HealthCheckResult.Healthy());
IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"];
builder.Services.AddValidation()
.AddEndpointValidation();
builder.Services.AddOpenApi(options =>
{
options.AddDocumentTransformer((document, context, cancellationToken) =>
{
document.Components ??= new OpenApiComponents();
document.Components?.SecuritySchemes!["OAuth2"] = new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Description = "Interactive OAuth2 flow for authentication and authorization",
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/authorize"),
TokenUrl = new Uri($"{builder.Configuration["SecuritySettings:Authority"]}/connect/token"),
Scopes = new Dictionary<string, string>
{
{ "openid", "OpenID Connect" },
{ "profile", "User Profile" },
{ "roles", "User Roles" },
{ "email", "User Email" },
{ "address", "User Address" },
{ builder.Configuration["SecuritySettings:Audience"]!, "PFM API Scope" }
}
}
}
};
return Task.CompletedTask;
});
options.AddOperationTransformer((operation, context, cancellationToken) =>
{
operation.Security ??= [];
var schemeReference = new OpenApiSecuritySchemeReference("OAuth2");
operation.Security.Add(new OpenApiSecurityRequirement
{
[schemeReference] = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
});
return Task.CompletedTask;
});
});
var allowedOrigins = builder.Configuration.GetSection("CorsOrigins").Get<string[]>() ?? [];
builder.Services.AddCors(options =>
{
options.AddPolicy(CorsPolicy, policy =>
{
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
var app = builder.Build(); var app = builder.Build();
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>(); var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
var scheduler = await schedulerFactory.GetScheduler(); var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName);
if (scheduler!.Status != SchedulerStatus.Running) if (!scheduler!.IsStarted)
await scheduler.Start(); await scheduler.Start();
app.UseHsts(); app.UseHsts();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(CorsPolicy);
app.UseOutputCache();
app.UseRouting();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
ApiVersionSet versionSet = app.NewApiVersionSet("v1") ApiVersionSet versionSet = app.NewApiVersionSet("v1")
.HasApiVersion(new ApiVersion(1)) .HasApiVersion(new ApiVersion(1))
.HasApiVersion(new ApiVersion(2))
.ReportApiVersions() .ReportApiVersions()
.Build(); .Build();
@@ -145,16 +63,7 @@ var versionGroups = new Dictionary<int, RouteGroupBuilder>
{ {
{ {
1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet) 1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet)
.RequireCors(CorsPolicy) .RequireAuthorization().AddEndpointFilter<ActiveUserFilter>()
.AddEndpointFilter(async (context, next) =>
{
if (HttpMethods.IsOptions(context.HttpContext.Request.Method))
return await next(context);
var filter = context.HttpContext.RequestServices.GetRequiredService<ActiveUserFilter>();
return await filter.InvokeAsync(context, next);
})
} }
}; };
@@ -177,25 +86,14 @@ var apiVersions = app.DescribeApiVersions()
.ToList(); .ToList();
foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion)) foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion))
{
app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) => app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) =>
{ {
options.AddServer(new ScalarServer($"https://{context.Request.Host}")); options.AddServer(new ScalarServer($"https://{context.Request.Host}"));
options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json"); options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json");
options.WithTheme(ScalarTheme.DeepSpace); options.WithTheme(ScalarTheme.DeepSpace);
options.Agent = new ScalarAgentOptions { Disabled = true }; options.Agent = new ScalarAgentOptions { Disabled = true };
options.AddPreferredSecuritySchemes("OAuth2") options.Authentication = new ScalarAuthenticationOptions { PreferredSecuritySchemes = ["Bearer"] };
.AddOAuth2Flows("OAuth2", flow =>
{
flow.AuthorizationCode = new AuthorizationCodeFlow
{
ClientId = builder.Configuration["SecuritySettings:ClientId"]!,
Pkce = Pkce.Sha256,
SelectedScopes = [.. identityScopes, builder.Configuration["SecuritySettings:Audience"]!]
};
}); });
});
}
var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1"; var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1";
+16 -19
View File
@@ -1,18 +1,25 @@
{ {
"EnableServiceCaching": true,
"CorsOrigins": [
"https://console.ifri.khongisa.co.za",
"https://localhost:7208"
],
"GeneralS3Settings": { "GeneralS3Settings": {
"ServiceUrl": "https://s3.garage.khongisa.co.za", "ServiceUrl": "http://192.168.1.177:30900",
"Region": "garage", "Region": "garage",
"BucketName": "pfm", "BucketName": "pfm.general",
"CdnBaseUrl": "https://pfm.cdn.khongisa.co.za" "CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
},
"ContractS3Settings": {
"ServiceUrl": "http://192.168.1.177:30900",
"Region": "garage",
"BucketName": "pfm.general",
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
},
"EvidenceS3Settings": {
"ServiceUrl": "http://192.168.1.177:30900",
"Region": "garage",
"BucketName": "pfm.general",
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
}, },
"Monitoring": { "Monitoring": {
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
"ServiceName": "Pfm.DEV" "ServiceName": "MidrandBooks.DEV"
}, },
"SecuritySettings": { "SecuritySettings": {
"Authority": "https://sts.security.khongisa.co.za", "Authority": "https://sts.security.khongisa.co.za",
@@ -24,15 +31,5 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"EmailProcessorCron": "0 */5 * * * ?",
"Email": {
"SmtpServer": "smtp.example.com",
"SmtpPort": 587,
"SenderEmail": "noreply@example.com",
"SenderName": "Example"
},
"HasherSettings": {
"MinHashLength": 11
},
"AllowedHosts": "*" "AllowedHosts": "*"
} }
@@ -1,5 +1,4 @@
using PostFundManagement.Application.Services.Shared; using PostFundManagement.Application.Shared;
using PostFundManagement.Domain.Errors;
namespace PostFundManagement.Application.Abstractions; namespace PostFundManagement.Application.Abstractions;
@@ -10,57 +9,17 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
protected abstract string BucketName { get; } protected abstract string BucketName { get; }
protected abstract string CdnBaseUrl { get; } protected abstract string CdnBaseUrl { get; }
public string GenerateCdnUrl(string fileKey) => $"{CdnBaseUrl}/{fileKey}"; public virtual async Task<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default)
{
public virtual async ValueTask<Result<string>> GeneratePresignedUrlAsync(string fileKey, TimeSpan duration, string? contentType = null, CancellationToken cancellationToken = default) => try
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
{ {
if (string.IsNullOrWhiteSpace(BucketName)) if (string.IsNullOrWhiteSpace(BucketName))
return Result.Fail(ValidationError.Create("Bucket name is not configured.")); return Result.Fail<string>("Bucket name is not configured.");
if (string.IsNullOrWhiteSpace(fileKey))
return Result.Fail(ValidationError.Create("File key is required."));
var request = new GetPreSignedUrlRequest
{
BucketName = BucketName,
Key = fileKey,
Expires = DateTime.UtcNow.Add(duration),
Verb = HttpVerb.GET
};
if (!string.IsNullOrWhiteSpace(contentType))
{
request.ResponseHeaderOverrides.ContentType = contentType;
request.ResponseHeaderOverrides.ContentDisposition = "inline";
}
var presignedUrl = await Client.GetPreSignedURLAsync(request);
return Result.FailIf(string.IsNullOrWhiteSpace(presignedUrl),
CreateError.Create("Failed to create presigned URL"))
.ToResult(presignedUrl);
}));
private static string CreateFileKey(string path, string fileName, string fileHash)
{
var cleanSubPath = path.Replace('\\', '/').Trim('/');
return string.IsNullOrWhiteSpace(cleanSubPath)
? $"{fileHash.ToLower(CultureInfo.InvariantCulture)}_{fileName}"
: $"{cleanSubPath}/{fileHash.ToLower(CultureInfo.InvariantCulture)}_{fileName}";
}
public virtual async ValueTask<Result<string>> UploadFileAsync(string fileName, string subPath, Stream fileStream, string contentType, CancellationToken cancellationToken = default) =>
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
{
if (string.IsNullOrWhiteSpace(BucketName))
return Result.Fail<string>(ValidationError.Create("Bucket name is not configured."));
if (string.IsNullOrWhiteSpace(CdnBaseUrl)) if (string.IsNullOrWhiteSpace(CdnBaseUrl))
return Result.Fail<string>(ValidationError.Create("CDN base URL is not configured.")); return Result.Fail<string>("CDN base URL is not configured.");
await using var stream = new MemoryStream(); using var stream = new MemoryStream();
await fileStream.CopyToAsync(stream, cancellationToken); await fileStream.CopyToAsync(stream, cancellationToken);
await fileStream.DisposeAsync(); await fileStream.DisposeAsync();
@@ -69,10 +28,10 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
var fileHash = HashService.StreamToSha256Hash(stream); var fileHash = HashService.StreamToSha256Hash(stream);
if (string.IsNullOrWhiteSpace(fileHash)) if(string.IsNullOrWhiteSpace(fileHash))
return Result.Fail<string>(CreateError.Create("Failed to compute file hash.")); return Result.Fail<string>("Failed to compute file hash.");
var fileKey = CreateFileKey(subPath, fileName, fileHash); var fileKey = $"{fileHash.ToLower(CultureInfo.InvariantCulture)}{Path.GetExtension(fileName)}";
var putRequest = new PutObjectRequest var putRequest = new PutObjectRequest
{ {
@@ -87,24 +46,38 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
var response = await Client.PutObjectAsync(putRequest, cancellationToken); var response = await Client.PutObjectAsync(putRequest, cancellationToken);
return response.HttpStatusCode != HttpStatusCode.OK return response.HttpStatusCode != System.Net.HttpStatusCode.OK
? Result.Fail<string>(CreateError.Create($"Failed to upload {fileName} to S3.")) ? Result.Fail<string>($"Failed to upload {fileName} to S3.")
: Result.Ok(fileKey); : Result.Ok($"{CdnBaseUrl}/{fileKey}");
})); }
catch (Exception ex)
{
return Result.Fail<string>(new Error($"Error uploading {fileName} to S3: {ex.Message}").CausedBy(ex));
}
}
public virtual async ValueTask<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) => public virtual async Task<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default)
await Result.Try((Func<ValueTask<Result>>)(async () => {
try
{ {
if (string.IsNullOrWhiteSpace(BucketName)) if (string.IsNullOrWhiteSpace(BucketName))
return Result.Fail(NotFoundError.Create("Bucket name is not configured.")); return Result.Fail("Bucket name is not configured.");
var response = await Client.DeleteObjectAsync(new DeleteObjectRequest var deleteRequest = new DeleteObjectRequest
{ {
BucketName = BucketName, BucketName = BucketName,
Key = fileKey Key = fileKey
}, cancellationToken); };
return Result.FailIf(response.HttpStatusCode == HttpStatusCode.NoContent, var response = await Client.DeleteObjectAsync(deleteRequest, cancellationToken);
UpdateError.Create($"Failed to delete {fileKey} from S3."));
})); return response.HttpStatusCode != System.Net.HttpStatusCode.NoContent
? Result.Fail($"Failed to delete {fileKey} from S3.")
: Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(new Error($"Error deleting {fileKey} from S3: {ex.Message}").CausedBy(ex));
}
}
} }
@@ -1,9 +0,0 @@
namespace PostFundManagement.Application.Abstractions;
public abstract class ServiceCacheBase
{
protected readonly DistributedCacheEntryOptions cacheOptions = new()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)
};
}
@@ -1,16 +0,0 @@
using PostFundManagement.Application.Services.Communications;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface ICommunicationsService
{
ValueTask<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default);
ValueTask<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default);
}
@@ -1,32 +0,0 @@
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IEvidenceService
{
ValueTask<Result> CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default);
ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result> CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken);
ValueTask<Result<Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default);
ValueTask<Result<Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<string>> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default);
}
@@ -1,26 +0,0 @@
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IExecutionService
{
ValueTask<Result<Risk[]>> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result> CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default);
}
@@ -1,32 +0,0 @@
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IGovernanceService
{
ValueTask<Result<Programme[]>> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Portfolio[]>> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Organisation[]>> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default);
ValueTask<Result> CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default);
ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default);
}
@@ -1,34 +0,0 @@
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IGrantsService
{
ValueTask<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Contract>> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Budget>> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default);
}
@@ -1,6 +0,0 @@
namespace PostFundManagement.Application.Abstractions.Services;
public interface IIdentityService
{
ValueTask<Result> UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default);
}
@@ -1,46 +0,0 @@
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IProjectService
{
ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Indicator[]>> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default);
ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default);
ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default);
ValueTask<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default);
ValueTask<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default);
}
@@ -1,6 +0,0 @@
namespace PostFundManagement.Application.Abstractions.Services;
public interface IToolService
{
ValueTask<Result<string>> EncodeLong(long id, CancellationToken cancellationToken);
}
@@ -1,16 +0,0 @@
using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Models;
namespace PostFundManagement.Application.Abstractions.Services;
public interface IUserService
{
ValueTask<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default);
ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default);
ValueTask<Result> UpdateUserStatusAsync(Guid userId, ActivityStatus status, CancellationToken cancellationToken = default);
ValueTask<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken);
}
@@ -1,3 +1,4 @@
using Asp.Versioning;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Security; using PostFundManagement.Domain.Security;
using PostFundManagement.Domain.Security.Configuration; using PostFundManagement.Domain.Security.Configuration;
@@ -6,23 +7,6 @@ namespace PostFundManagement.Application.Extensions;
public static class Api public static class Api
{ {
public static IServiceCollection AddEndpointValidation(this IServiceCollection services) => services.AddValidation();
public static IServiceCollection AddEndpointAndServiceCaching(this IServiceCollection services, IConfiguration configuration)
{
services.AddStackExchangeRedisOutputCache(options =>
{
options.Configuration = configuration.GetConnectionString("Redis");
options.ConfigurationOptions?.ClientName = "PFM-API";
options.InstanceName = "PFM-API_";
});
services.AddOutputCache(options =>
options.AddBasePolicy(builder => builder.Expire(TimeSpan.FromMinutes(2))));
return services;
}
public static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) => public static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) =>
services.Configure<CookiePolicyOptions>(options => services.Configure<CookiePolicyOptions>(options =>
{ {
@@ -1,39 +1,48 @@
namespace PostFundManagement.Application.Extensions; using PostFundManagement.Application.Abstractions;
using PostFundManagement.Domain.Abstractions;
namespace PostFundManagement.Application.Extensions;
public static class General public static class General
{ {
public static string Clean(this string input) => Patterns.SpaceRegex().Replace(input, string.Empty); public static IServiceCollection AddRegisterServices(this IServiceCollection services)
{
var serviceType = typeof(IService);
public static string ToJson<TValue>(this TValue obj) where TValue : class => JsonSerializer.Serialize(obj); var sharedImplementations = typeof(IApplication).Assembly.GetTypes()
.Where(t => serviceType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
public static TValue FromJson<TValue>(this string json) where TValue : class => JsonSerializer.Deserialize<TValue>(json)!; foreach (var sharedImplementation in sharedImplementations) services.AddScoped(sharedImplementation);
return services;
}
public static Guid GetUserId(this HttpContext context) => (Guid)context.Items[Domain.Extensions.Constants.UserIdItemKey]!; public static Guid GetUserId(this HttpContext context) => (Guid)context.Items[Domain.Extensions.Constants.UserIdItemKey]!;
public static string GetUserEmail(this HttpContext context) => (string)context.Items[Domain.Extensions.Constants.EmailtemKey]!; public static string GetUserEmail(this HttpContext context) => (string)context.Items[Domain.Extensions.Constants.EmaiItemKey]!;
public static (bool Decoded, Guid userId) GetUserId(this ClaimsPrincipal principal) public static (bool Decoded, Guid userId) GetUserId(this ClaimsPrincipal principal)
{ {
var subClaim = principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value ?? principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; var sidClaim = principal.FindFirst("sid")?.Value ?? principal.FindFirst(ClaimTypes.Sid)?.Value;
var decoded = Guid.TryParse(subClaim, out var userId); var decoded = Guid.TryParse(sidClaim, out var userId);
return (decoded, userId); return (decoded, userId);
} }
public static (bool Decoded, string email) GetUserEmail(this ClaimsPrincipal principal) public static (bool Decoded, Guid email) GetUserEmail(this ClaimsPrincipal principal)
{ {
var emailClaim = principal.FindFirst("email")?.Value ?? principal.FindFirst(ClaimTypes.Email)?.Value; var emailClaim = principal.FindFirst("email")?.Value ?? principal.FindFirst(ClaimTypes.Email)?.Value;
var decoded = !string.IsNullOrWhiteSpace(emailClaim); var decoded = Guid.TryParse(emailClaim, out var email);
return (decoded, emailClaim!); return (decoded, email);
} }
public static Microsoft.AspNetCore.Mvc.ProblemDetails ToProblems(this IReadOnlyList<IError> errors) => new() public static Microsoft.AspNetCore.Mvc.ProblemDetails ToProblems(this IReadOnlyList<IError> errors) => new()
{ {
Detail = string.Join(',', errors.Select(e => e.Message)), Detail = string.Join(',', errors.Select(e => e.Message)),
Status = StatusCodes.Status400BadRequest, Status = StatusCodes.Status400BadRequest,
Title = "Error", Title = errors[0]?.Message,
}; };
} }
@@ -1,4 +1,4 @@
using PostFundManagement.Application.Services.Shared; using PostFundManagement.Application.Shared;
using PostFundManagement.Domain.Configuration.Hash; using PostFundManagement.Domain.Configuration.Hash;
namespace PostFundManagement.Application.Extensions; namespace PostFundManagement.Application.Extensions;

Some files were not shown because too many files have changed in this diff Show More