14 Commits
Author SHA1 Message Date
khwezi 2c13170859 Merge pull request 'Endpoints' (#13) from endpoints into main
Reviewed-on: #13
2026-09-02 16:14:34 +02:00
khwezi d87c8aa3c9 Merge pull request 'Updated configurations' (#12) from endpoints into main
Reviewed-on: #12
2026-09-02 09:45:38 +02:00
khwezi 6af7815ad1 Merge pull request 'Removed node tooling install' (#11) from endpoints into main
Reviewed-on: #11
2026-09-02 09:30:06 +02:00
khwezi a169f6c3f6 Merge pull request 'Endpoints' (#10) from endpoints into main
Reviewed-on: #10
2026-09-02 09:28:17 +02:00
khwezi 65ea753c19 Merge pull request 'Endpoints' (#9) from endpoints into main
Reviewed-on: #9
2026-09-01 12:29:47 +02:00
khwezi e6d2663265 Merge pull request 'Endpoints' (#8) from endpoints into main
Reviewed-on: #8
2026-08-30 17:47:24 +02:00
khwezi 91b5814b60 Merge pull request 'Endpoints' (#7) from endpoints into main
Reviewed-on: #7
2026-08-30 16:27:37 +02:00
khwezi 0c4c848568 Merge pull request 'Endpoints' (#6) from endpoints into main
Reviewed-on: #6
2026-08-30 16:11:53 +02:00
khwezi fb05150ebf Merge pull request 'Endpoints' (#5) from endpoints into main
Reviewed-on: #5
2026-08-30 15:47:32 +02:00
khwezi 2f9c20020b Merge pull request 'Reogranised API and Security assets' (#4) from endpoints into main
Reviewed-on: #4
2026-08-30 15:09:32 +02:00
khwezi a771957db3 Merge pull request 'Endpoints' (#3) from endpoints into main
Reviewed-on: #3
2026-08-30 14:54:58 +02:00
khwezi 46267a2a34 Merge pull request 'Endpoints' (#2) from endpoints into main
Reviewed-on: #2
2026-08-30 14:26:08 +02:00
khwezi a7f360cc2a Merge pull request 'Security' (#1) from security into main
Reviewed-on: #1
2026-08-21 11:47:36 +02:00
hermes 67c08b40ca Refactored gitignore 2026-08-21 11:45:33 +02:00
225 changed files with 701 additions and 46710 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
+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
+28
View File
@@ -0,0 +1,28 @@
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM node:22-bookworm-slim AS node
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG TARGETARCH
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \
NUGET_XMLDOC_MODE=skip
WORKDIR /app_source
COPY . .
RUN dotnet restore "PostFundManagement.slnx"
WORKDIR "/app_source/PostFundManagement.Api"
RUN dotnet build "PostFundManagement.Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "PostFundManagement.Api.csproj" -c Release --no-restore -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
ENTRYPOINT ["dotnet", "PostFundManagement.Api.dll"]
-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,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,16 +14,15 @@ 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 await service.GetApplicantInviteAsync(userId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var invite } => Results.Ok(invite), { IsSuccess: true, Value: var invite } => Results.Ok(invite),
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()),
}) })
.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)
@@ -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,5 +1,5 @@
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;
@@ -15,7 +15,7 @@ public sealed 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 await service.GetInvitesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -23,7 +23,6 @@ public sealed class GetInvitesEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -16,7 +15,7 @@ public sealed 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 await service.SendInviteAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var invite } => Results.Ok(invite), { IsSuccess: true, Value: var invite } => Results.Ok(invite),
@@ -24,7 +23,6 @@ public sealed class SendInviteEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Invite>(StatusCodes.Status200OK)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -13,7 +13,7 @@ public class CreateEvidenceEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/evidence/file", async (HttpRequest request, IEvidenceService service, builder.MapPut("api/evidence/file", async (HttpRequest request, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
{ {
var userId = request.HttpContext.GetUserId(); var userId = request.HttpContext.GetUserId();
@@ -34,9 +34,9 @@ public class CreateEvidenceEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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(CreateEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,7 +23,6 @@ public class CreateIndicatorEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,7 +23,6 @@ public class CreateMilestoneEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -13,7 +13,7 @@ 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 await service.GetEvidenceAsync(evidenceId, cancellationToken) switch
{ {
@@ -23,7 +23,6 @@ public sealed class GetEvidenceEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -1,5 +1,5 @@
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;
@@ -13,7 +13,7 @@ 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 await service.GetEvidenceFilesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -21,7 +21,6 @@ public class GetEvidenceFilesEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -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,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetIndicatorAsync(indicatorId, cancellationToken) switch
{ {
@@ -23,16 +23,14 @@ public sealed class GetIndicatorEndpoint : IEndpoint
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
.Produces(StatusCodes.Status403Forbidden) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence);
.WithTags(EndpointTags.Evidence)
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,7 +14,7 @@ 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 await service.GetIndicatorsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -22,13 +22,11 @@ public class GetIndicatorsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Evidence) .WithTags(EndpointTags.Evidence);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -13,7 +13,7 @@ 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 await service.GetMilestoneEvidencesAsync(milestoneId, cancellationToken) switch
{ {
@@ -22,7 +22,6 @@ public class GetMilestoneEvidenceEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -13,7 +13,7 @@ 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 await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
{ {
@@ -22,7 +22,6 @@ public class GetProjectEvidenceEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()),
}) })
.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)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.RecordIndicatorActualAmountAsync(indicatorId, actualAmount, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +23,6 @@ public class RecordIndicatorAmountEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,5 +1,4 @@
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.Errors;
@@ -14,21 +13,19 @@ public sealed class UpdateEvidenceEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/evidence", async (UpdateEvidenceRequest request, HttpContext context, IEvidenceService service, builder.MapPut("api/evidence", async (UpdateEvidenceRequest request, HttpContext context, EvidenceService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { 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 when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Updates a specific evidence record metadata") .WithDescription("Updates a specific evidence record metadata")
.RequireAuthorization(Constants.MainScope)
.DisableAntiforgery()
.WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName()) .WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces<Domain.Models.Evidence>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.ApproveDisbursementAsync(disbursementId, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +23,6 @@ public class ApproveDisbursementEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,7 +23,6 @@ public class CreateDisbursementEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -14,7 +13,7 @@ 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 await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
{ {
@@ -24,7 +23,6 @@ public class CreateRiskEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ public sealed class GetDisbursementEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/execution/disbursements/{disbursementId}", async (string disbursementId, IExecutionService service, builder.MapGet("api/execution/disbursements/{disbursementId}", async (string disbursementId, ExecutionService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetDisbursementAsync(disbursementIdHash: disbursementId, cancellationToken) switch await service.GetDisbursementAsync(disbursementIdHash: disbursementId, cancellationToken) switch
{ {
@@ -24,7 +24,6 @@ public sealed class GetDisbursementEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get a specific disbursement") .WithDescription("Get a specific disbursement")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetDisbursementEndpoint).ToEndpointName()) .WithName(typeof(GetDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Disbursement>(StatusCodes.Status200OK) .Produces<Disbursement>(StatusCodes.Status200OK)
@@ -32,7 +31,6 @@ public sealed class GetDisbursementEndpoint : IEndpoint
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution) .WithTags(EndpointTags.Execution);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,7 +14,7 @@ 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 await service.GetDisbursementsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -22,14 +22,12 @@ public class GetDisbursementsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution) .WithTags(EndpointTags.Execution);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ public sealed class GetRiskEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/execution/risks/{riskId}", async (string riskId, IExecutionService service, builder.MapGet("api/execution/risks/{riskId}", async (string riskId, ExecutionService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetRiskAsync(riskIdHash: riskId, cancellationToken) switch await service.GetRiskAsync(riskIdHash: riskId, cancellationToken) switch
{ {
@@ -24,14 +24,12 @@ public sealed class GetRiskEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get a specific risk") .WithDescription("Get a specific risk")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetRiskEndpoint).ToEndpointName()) .WithName(typeof(GetRiskEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Risk>(StatusCodes.Status200OK) .Produces<Risk>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution) .WithTags(EndpointTags.Execution);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -13,7 +13,7 @@ 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 await service.GetRisksAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -21,13 +21,11 @@ public class GetRisksEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Execution) .WithTags(EndpointTags.Execution);
.CacheOutput();
} }
} }
@@ -1,5 +1,4 @@
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.Errors;
@@ -15,7 +14,7 @@ public sealed class UpdateDisbursementEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/execution/disbursements", async (UpdateDisbursementRequest request, HttpContext context, builder.MapPut("api/execution/disbursements", async (UpdateDisbursementRequest request, HttpContext context,
IExecutionService service, CancellationToken cancellationToken = default) => ExecutionService service, CancellationToken cancellationToken = default) =>
await service.UpdateDisbursementAsync(request, userId: context.GetUserId(), cancellationToken) switch await service.UpdateDisbursementAsync(request, userId: context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +22,6 @@ public sealed class UpdateDisbursementEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Update the details of a specific disbursement") .WithDescription("Update the details of a specific disbursement")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName()) .WithName(typeof(UpdateDisbursementEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
@@ -1,5 +1,4 @@
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.Errors;
@@ -15,7 +14,7 @@ public sealed class UpdateRiskEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/execution/risks", async (UpdateRiskRequest request, builder.MapPut("api/execution/risks", async (UpdateRiskRequest request,
IExecutionService service, CancellationToken cancellationToken = default) => ExecutionService service, CancellationToken cancellationToken = default) =>
await service.UpdateRiskAsync(request, cancellationToken) switch await service.UpdateRiskAsync(request, cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +22,6 @@ public sealed class UpdateRiskEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Update the details of a specific risk") .WithDescription("Update the details of a specific risk")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateRiskEndpoint).ToEndpointName()) .WithName(typeof(UpdateRiskEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ public sealed 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 await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +22,6 @@ public sealed class CreateOrganisationEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -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,4 +1,3 @@
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;
@@ -13,7 +12,7 @@ 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 await service.GetDashboardAsync(cancellationToken) switch
{ {
@@ -21,13 +20,11 @@ public class DashboardEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance) .WithTags(EndpointTags.Governance);
.CacheOutput();
} }
} }
@@ -1,7 +1,6 @@
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;
@@ -14,23 +13,19 @@ 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 await service.GetInstrumentsAsync(cancellationToken) switch
{ {
{ IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs), { IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()), var r => Results.InternalServerError(r.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(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance) .Produces(StatusCodes.Status401Unauthorized)
.CacheOutput(); .WithTags(EndpointTags.Governance);
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -15,16 +15,15 @@ public class GetOrganisationEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/governance/organisations/{organisationId}", async (string organisationId, builder.MapGet("api/governance/organisations/{organisationId}", async (string organisationId,
IGovernanceService service, CancellationToken cancellationToken = default) => GovernanceService service, CancellationToken cancellationToken = default) =>
await service.GetOrganisationAsync(organisationId, cancellationToken) switch await service.GetOrganisationAsync(organisationId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var organisation } => Results.Ok(organisation), { IsSuccess: true, Value: var organisation } => Results.Ok(organisationId),
var r when r.HasError<IdDecodeError>() => Results.Forbid(), var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get organization profile by ID") .WithDescription("Get organization profile by ID")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetOrganisationEndpoint).ToEndpointName()) .WithName(typeof(GetOrganisationEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Organisation>(StatusCodes.Status200OK) .Produces<Organisation>(StatusCodes.Status200OK)
@@ -32,7 +31,6 @@ public class GetOrganisationEndpoint : IEndpoint
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance) .WithTags(EndpointTags.Governance);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,7 +14,7 @@ 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 await service.GetOrganisationsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -22,13 +22,11 @@ public class GetOrganisationsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError)
.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,7 +1,6 @@
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;
@@ -14,23 +13,19 @@ 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 await service.GetPorfoliosAsync(cancellationToken) switch
{ {
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios), { IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance) .Produces(StatusCodes.Status401Unauthorized)
.CacheOutput(); .WithTags(EndpointTags.Governance);
} }
} }
@@ -1,7 +1,6 @@
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;
@@ -14,23 +13,19 @@ 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 await service.GetProgrammesAsync(cancellationToken) switch
{ {
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes), { IsSuccess: true, Value: var programmes } => Results.Ok(programmes),
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Governance) .WithTags(EndpointTags.Governance);
.CacheOutput();
} }
} }
@@ -1,5 +1,4 @@
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.Errors;
@@ -15,7 +14,7 @@ public sealed class UpdateOrganisationEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request, builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
IGovernanceService service, CancellationToken cancellationToken = default) => GovernanceService service, CancellationToken cancellationToken = default) =>
await service.UpdateOrganisationAsync(request, cancellationToken) switch await service.UpdateOrganisationAsync(request, cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +22,6 @@ public sealed class UpdateOrganisationEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Update an existing organization profile") .WithDescription("Update an existing organization profile")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName()) .WithName(typeof(UpdateOrganisationEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status200OK)
@@ -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,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -25,7 +24,6 @@ public class CreateAwardEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateBudgetAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,16 +23,15 @@ public class CreateBudgetEndpoint : IEndpoint
var r when r.HasError<CreateError>() => Results.BadRequest(), var r when r.HasError<CreateError>() => Results.BadRequest(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest)
.Produces(StatusCodes.Status400BadRequest) .Produces<Problems>(StatusCodes.Status400BadRequest)
.Produces<Problems>(StatusCodes.Status400BadRequest) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Grants);
.WithTags(EndpointTags.Grants);
} }
} }
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,7 +23,6 @@ public class CreateContractEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.ExecuteContractAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +22,6 @@ public class ExecuteContractEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,5 +1,5 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors; using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ public class GetAwardEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/awards/{awardId}", async (string awardId, IGrantsService service, builder.MapGet("api/grants/awards/{awardId}", async (string awardId, GrantsService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetAwardAsync(awardId, cancellationToken) switch await service.GetAwardAsync(awardId, cancellationToken) switch
{ {
@@ -23,16 +23,14 @@ public class GetAwardEndpoint : IEndpoint
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get award by ID") .WithDescription("Get award by ID")
.RequireAuthorization(Constants.MainScope) .WithName(typeof(GetAwardEndpoint).ToEndpointName())
.WithName(typeof(GetAwardEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1))
.MapToApiVersion(new ApiVersion(1)) .Produces<Award>(StatusCodes.Status200OK)
.Produces<Award>(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status404NotFound) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants);
.WithTags(EndpointTags.Grants)
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,21 +14,19 @@ 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 await service.GetAwardsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var awards } => Results.Ok(awards), { IsSuccess: true, Value: var awards } => Results.Ok(awards),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Grants);
.WithTags(EndpointTags.Grants)
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors; using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ public class GetBudgetEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/budgets/{budgetId}", async (string budgetId, IGrantsService service, builder.MapGet("api/grants/budgets/{budgetId}", async (string budgetId, GrantsService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetBudgetAsync(budgetId, cancellationToken) switch await service.GetBudgetAsync(budgetId, cancellationToken) switch
{ {
@@ -23,16 +23,14 @@ public class GetBudgetEndpoint : IEndpoint
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get budget by ID") .WithDescription("Get budget by ID")
.RequireAuthorization(Constants.MainScope) .WithName(typeof(GetBudgetEndpoint).ToEndpointName())
.WithName(typeof(GetBudgetEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1))
.MapToApiVersion(new ApiVersion(1)) .Produces<Budget>(StatusCodes.Status200OK)
.Produces<Budget>(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants);
.WithTags(EndpointTags.Grants)
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,21 +14,19 @@ 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 await service.GetBudgetsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var budgets } => Results.Ok(budgets), { IsSuccess: true, Value: var budgets } => Results.Ok(budgets),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Grants);
.WithTags(EndpointTags.Grants)
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Extensions; using PostFundManagement.Application.Extensions;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Domain.Abstractions; using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors; using PostFundManagement.Domain.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ public class GetContractEndpoint : IEndpoint
{ {
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/grants/contracts/{contractId}", async (string contractId, IGrantsService service, builder.MapGet("api/grants/contracts/{contractId}", async (string contractId, GrantsService service,
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
await service.GetContractAsync(contractId, cancellationToken) switch await service.GetContractAsync(contractId, cancellationToken) switch
{ {
@@ -23,16 +23,14 @@ public class GetContractEndpoint : IEndpoint
var r when r.HasError<NotFoundError>() => Results.NotFound(), var r when r.HasError<NotFoundError>() => Results.NotFound(),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get contract by ID") .WithDescription("Get contract by ID")
.RequireAuthorization(Constants.MainScope) .WithName(typeof(GetContractEndpoint).ToEndpointName())
.WithName(typeof(GetContractEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1))
.MapToApiVersion(new ApiVersion(1)) .Produces<Contract>(StatusCodes.Status200OK)
.Produces<Contract>(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants);
.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,5 +1,5 @@
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;
@@ -14,7 +14,7 @@ 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 await service.GetContractsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -22,13 +22,11 @@ public class GetContractsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError)
.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,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } result => Results.Ok(), { IsSuccess: true } result => Results.Ok(),
@@ -23,7 +22,6 @@ public class CreateActivityEndpoint : IEndpoint
var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError<CreateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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))
@@ -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,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } result => Results.Ok(), { IsSuccess: true } result => Results.Ok(),
@@ -25,7 +24,6 @@ public class CreateProjectEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetActivitiesAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var activities } => Results.Ok(activities), { IsSuccess: true, Value: var activities } => Results.Ok(activities),
@@ -22,14 +22,12 @@ public class GetActivitiesEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetActivityAsync(activityId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var activity } => Results.Ok(activity), { IsSuccess: true, Value: var activity } => Results.Ok(activity),
@@ -23,7 +23,6 @@ public class GetActivityEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -31,7 +30,6 @@ public class GetActivityEndpoint : IEndpoint
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetMilestoneAsync(milestoneId, cancellationToken) switch
{ {
@@ -24,7 +24,6 @@ public class GetMilestoneEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -1,5 +1,5 @@
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;
@@ -14,21 +14,20 @@ 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 await service.GetMilestonesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones), { IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError)
.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,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetProjectAsync(projectId, cancellationToken) switch
{ {
@@ -24,7 +24,6 @@ public class GetProjectEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -32,7 +31,6 @@ public class GetProjectEndpoint : IEndpoint
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken) switch
{ {
@@ -23,7 +23,6 @@ public class GetProjectIndicatorsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -31,7 +30,6 @@ public class GetProjectIndicatorsEndpoint : IEndpoint
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -15,7 +15,7 @@ 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 await service.GetProjectMilestonesAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones), { IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
@@ -23,7 +23,6 @@ public class GetProjectMilestonesEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -31,7 +30,6 @@ public class GetProjectMilestonesEndpoint : IEndpoint
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -15,7 +15,7 @@ public class GetProjectRisksEndpoint : IEndpoint
public void Map(IEndpointRouteBuilder builder) public void Map(IEndpointRouteBuilder builder)
{ {
builder.MapGet("api/projects/{projectId}/risks", async (string projectId, builder.MapGet("api/projects/{projectId}/risks", async (string projectId,
IProjectService service, CancellationToken cancellationToken = default) => ProjectService service, CancellationToken cancellationToken = default) =>
await service.GetProjectRisksAsync(projectId, cancellationToken) switch await service.GetProjectRisksAsync(projectId, cancellationToken) switch
{ {
{ IsSuccess: true, Value: var risks } => Results.Ok(risks), { IsSuccess: true, Value: var risks } => Results.Ok(risks),
@@ -23,7 +23,6 @@ public class GetProjectRisksEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.WithDescription("Get a list of risks for a project") .WithDescription("Get a list of risks for a project")
.RequireAuthorization(Constants.MainScope)
.WithName(typeof(GetProjectRisksEndpoint).ToEndpointName()) .WithName(typeof(GetProjectRisksEndpoint).ToEndpointName())
.MapToApiVersion(new ApiVersion(1)) .MapToApiVersion(new ApiVersion(1))
.Produces<Risk[]>(StatusCodes.Status200OK) .Produces<Risk[]>(StatusCodes.Status200OK)
@@ -31,7 +30,6 @@ public class GetProjectRisksEndpoint : IEndpoint
.Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,5 @@
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;
@@ -14,7 +14,7 @@ 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 await service.GetProjectsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
{ {
@@ -22,13 +22,11 @@ public class GetProjectsEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status500InternalServerError)
.Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status401Unauthorized)
.WithTags(EndpointTags.Projects) .WithTags(EndpointTags.Projects);
.CacheOutput();
} }
} }
@@ -1,5 +1,4 @@
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;
@@ -13,7 +12,7 @@ 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 await service.UpdateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
{ {
@@ -21,7 +20,6 @@ public sealed class UpdateMilestoneEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -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,5 +1,4 @@
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.Errors;
@@ -15,21 +14,20 @@ 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 await service.UpdateProjectAsync(request, userId: context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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<Problems>(StatusCodes.Status400BadRequest) .Produces<Problems>(StatusCodes.Status500InternalServerError)
.Produces<Problems>(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Projects);
.WithTags(EndpointTags.Projects);
} }
} }
@@ -1,4 +1,3 @@
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;
@@ -15,7 +14,7 @@ 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 await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -24,7 +23,6 @@ public class UpdateProjectStatusEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.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)
@@ -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,5 +1,5 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -14,7 +14,7 @@ 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 await service.GetUserAsync(userId, cancellationToken) switch
{ {
@@ -23,7 +23,6 @@ public sealed class GetUserEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -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;
@@ -15,7 +14,7 @@ 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 await service.GetUsersAsync(Pagination.Create(page, pageSize), request, cancellationToken) switch
{ {
@@ -23,7 +22,6 @@ public class GetUsersEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -1,6 +1,6 @@
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.Errors;
using PostFundManagement.Domain.Extensions; using PostFundManagement.Domain.Extensions;
@@ -15,7 +15,7 @@ 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);
@@ -30,7 +30,6 @@ public class SyncUserEndpoint : IEndpoint
}; };
}) })
.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)
@@ -1,5 +1,5 @@
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.Errors;
@@ -15,7 +15,7 @@ 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 await service.UpdateUserStatusAsync(userId, status, cancellationToken) switch
{ {
{ IsSuccess: true } => Results.Ok(), { IsSuccess: true } => Results.Ok(),
@@ -23,7 +23,6 @@ public sealed class UpdateUserStatusEndpoint : IEndpoint
var r => Results.InternalServerError(r.Errors.ToProblems()) var r => Results.InternalServerError(r.Errors.ToProblems())
}) })
.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)
@@ -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" />
@@ -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.2" />
<Using Include="Scalar.AspNetCore" /> <Using Include="Scalar.AspNetCore" />
<Using Include="Microsoft.OpenApi" /> <Using Include="Microsoft.OpenApi" />
+10 -53
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);
@@ -57,9 +40,6 @@ builder.Services.AddHealthChecks()
IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"]; IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"];
builder.Services.AddValidation()
.AddEndpointValidation();
builder.Services.AddOpenApi(options => builder.Services.AddOpenApi(options =>
{ {
options.AddDocumentTransformer((document, context, cancellationToken) => options.AddDocumentTransformer((document, context, cancellationToken) =>
@@ -106,38 +86,24 @@ builder.Services.AddOpenApi(options =>
}); });
}); });
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 +111,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("ApiScope").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);
})
} }
}; };
+15 -8
View File
@@ -1,14 +1,21 @@
{ {
"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",
@@ -10,55 +10,14 @@ 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 ValueTask<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) =>
await Result.Try((Func<ValueTask<Result<string>>>)(async () => 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>(Domain.Errors.ValidationError.Create("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>(Domain.Errors.ValidationError.Create("CDN base URL is not configured."));
await using var stream = new MemoryStream(); await using var stream = new MemoryStream();
@@ -72,7 +31,7 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
if (string.IsNullOrWhiteSpace(fileHash)) if (string.IsNullOrWhiteSpace(fileHash))
return Result.Fail<string>(CreateError.Create("Failed to compute file hash.")); return Result.Fail<string>(CreateError.Create("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
{ {
@@ -89,7 +48,7 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
return response.HttpStatusCode != HttpStatusCode.OK return response.HttpStatusCode != HttpStatusCode.OK
? Result.Fail<string>(CreateError.Create($"Failed to upload {fileName} to S3.")) ? Result.Fail<string>(CreateError.Create($"Failed to upload {fileName} to S3."))
: Result.Ok(fileKey); : Result.Ok($"{CdnBaseUrl}/{fileKey}");
})); }));
public virtual async ValueTask<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) => public virtual async ValueTask<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) =>
@@ -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);
}
@@ -6,23 +6,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,12 +1,21 @@
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]!;
@@ -34,6 +43,6 @@ public static class General
{ {
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,16 +0,0 @@
namespace PostFundManagement.Application.Extensions;
public partial class Patterns
{
[GeneratedRegex(@"\s+")]
public static partial Regex SpaceRegex();
[GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
public static partial Regex HexHashRegex { get; }
[GeneratedRegex(@"\A[0-9a-fA-F]{32}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
public static partial Regex Md5Regex { get; }
[GeneratedRegex(@"\A[0-9a-fA-F]{64}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
public static partial Regex Sha256Regex { get; }
}
@@ -2,7 +2,7 @@ using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Quartz; using PostFundManagement.Domain.Quartz;
using static PostFundManagement.Domain.Extensions.Constants; using static PostFundManagement.Domain.Extensions.Constants;
namespace PostFundManagement.Application.Extensions; namespace PostFundManagement.Domain.Extensions;
public static class Quartz public static class Quartz
{ {
@@ -10,27 +10,29 @@ public static class Quartz
{ {
var connectionString = configuration.GetConnectionString(DatabaseConfigName); var connectionString = configuration.GetConnectionString(DatabaseConfigName);
services.Configure<QuartzOptions>(options =>
{
options.Properties["quartz.scheduler.instanceName"] = schedulerName;
options.Properties["quartz.scheduler.instanceId"] = "AUTO";
});
services.ConfigureCommon(); services.ConfigureCommon();
services.AddQuartz(config => services.AddQuartz(config =>
{ {
config.SchedulerName = schedulerName;
config.SchedulerId = "AUTO";
config.UseSimpleTypeLoader(); config.UseSimpleTypeLoader();
config.UseDefaultThreadPool(options => options.MaxConcurrency = 0); config.UseDefaultThreadPool(options => options.MaxConcurrency = 0);
config.UseTimeZoneConverter(); config.UseTimeZoneConverter();
config.UsePersistentStore(storage => config.UsePersistentStore(storage =>
{ {
storage.ProvisionSchema(); storage.PerformSchemaValidation = false;
storage.UsePostgres(options => options.ConnectionString = connectionString!);
storage.UseSystemTextJsonSerializer(); storage.UseSystemTextJsonSerializer();
storage.SetProperty("quartz.jobStore.clustered", "true");
storage.UsePostgres(options =>
{
options.ConnectionString = connectionString!;
options.TablePrefix = "quartz.qrtz_";
});
storage.UseClustering(cluster => storage.UseClustering(cluster =>
{ {
cluster.CheckinInterval = TimeSpan.FromSeconds(30); cluster.CheckinInterval = TimeSpan.FromSeconds(30);
@@ -46,37 +48,36 @@ public static class Quartz
{ {
var connectionString = configuration.GetConnectionString(DatabaseConfigName); var connectionString = configuration.GetConnectionString(DatabaseConfigName);
services.Configure<QuartzOptions>(options =>
{
options.Properties["quartz.scheduler.instanceName"] = schedulerName;
options.Properties["quartz.scheduler.instanceId"] = "AUTO";
options.Properties["quartz.scheduler.batchTriggerAcquisitionMaxCount"] = "5";
options.Properties["quartz.jobStore.misfireThreshold"] = TimeSpan.FromMinutes(2).TotalMilliseconds.ToString(CultureInfo.InvariantCulture);
options.Properties["quartz.plugin.jobHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz.Plugins";
options.Properties["quartz.plugin.triggerHistory.type"] = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz.Plugins";
});
services.ConfigureCommon(); services.ConfigureCommon();
services.AddQuartzHostedService(options => services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
{
options.WaitForJobsToComplete = true;
});
services.AddQuartz(config => services.AddQuartz(config =>
{ {
config.SchedulerName = schedulerName;
config.SchedulerId = "AUTO";
config.InterruptJobsOnShutdown = true;
config.InterruptJobsOnShutdownWithWait = true;
config.MaxBatchSize = 5;
config.UseSimpleTypeLoader(); config.UseSimpleTypeLoader();
config.UseDefaultThreadPool(options => options.MaxConcurrency = 5); config.UseDefaultThreadPool(options => options.MaxConcurrency = 1);
config.UseTimeZoneConverter(); config.UseTimeZoneConverter();
config.SetProperty("quartz.jobStore.misfireThreshold", TimeSpan.FromMinutes(2).TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
config.UsePersistentStore(storage => config.UsePersistentStore(storage =>
{ {
storage.ProvisionSchema(); storage.PerformSchemaValidation = false;
storage.UsePostgres(options => options.ConnectionString = connectionString!);
storage.UseSystemTextJsonSerializer(); storage.UseSystemTextJsonSerializer();
storage.SetProperty("quartz.jobStore.clustered", "true");
storage.UsePostgres(options =>
{
options.ConnectionString = connectionString!;
options.TablePrefix = "quartz.qrtz_";
});
storage.UseClustering(cluster => storage.UseClustering(cluster =>
{ {
cluster.CheckinInterval = TimeSpan.FromSeconds(30); cluster.CheckinInterval = TimeSpan.FromSeconds(30);
@@ -90,7 +91,15 @@ public static class Quartz
private static IServiceCollection ConfigureCommon(this IServiceCollection services) private static IServiceCollection ConfigureCommon(this IServiceCollection services)
{ {
services.Configure<QuartzOptions>(options => options.Scheduling.IgnoreDuplicates = true); services.Configure<QuartzOptions>(options =>
{
options.Scheduling.IgnoreDuplicates = true;
options.Scheduling.OverWriteExistingData = true;
options["quartz.plugin.jobHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz.Plugins";
options["quartz.plugin.triggerHistory.type"] = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz.Plugins";
});
services.AddTransient<RetryJobListener>(); services.AddTransient<RetryJobListener>();
services.AddTransient<IJobOrchestrator, JobOrchestrator>(); services.AddTransient<IJobOrchestrator, JobOrchestrator>();
@@ -25,6 +25,40 @@ public static class S3
services.AddKeyedScoped<IS3Service, GeneralS3Service>(GeneralBucketName); services.AddKeyedScoped<IS3Service, GeneralS3Service>(GeneralBucketName);
} }
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value))
{
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(EvidenceBucketName, (provider, client) =>
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{EvidenceS3SettingsSection}:AccessKey").Value,
configuration.GetSection($"{EvidenceS3SettingsSection}:SecretKey").Value),
new AmazonS3Config
{
ServiceURL = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value,
AuthenticationRegion = configuration.GetSection($"{EvidenceS3SettingsSection}:Region").Value,
ForcePathStyle = true,
EndpointDiscoveryEnabled = true,
UseHttp = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
}));
services.AddKeyedScoped<IS3Service, EvidenceS3Service>(EvidenceBucketName);
}
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value))
{
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(ContractBucketName, (provider, client) =>
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{ContractS3SettingsSection}:AccessKey").Value,
configuration.GetSection($"{ContractS3SettingsSection}:SecretKey").Value),
new AmazonS3Config
{
ServiceURL = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value,
AuthenticationRegion = configuration.GetSection($"{ContractS3SettingsSection}:Region").Value,
ForcePathStyle = true,
EndpointDiscoveryEnabled = true,
UseHttp = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
}));
services.AddKeyedScoped<IS3Service, ContractS3Service>(ContractBucketName);
}
return services; return services;
} }
} }
@@ -1,6 +1,5 @@
using PostFundManagement.Application.Services.Shared; using PostFundManagement.Application.Services.Shared;
using PostFundManagement.Domain.Security.Configuration; using PostFundManagement.Domain.Security.Configuration;
using PostFundManagement.Domain.Extensions;
using PostFundManagement.Domain.Security.Sdk; using PostFundManagement.Domain.Security.Sdk;
using PostFundManagement.Infrastructure.Database; using PostFundManagement.Infrastructure.Database;
@@ -98,7 +97,6 @@ public static class Security
options.Scope.Clear(); options.Scope.Clear();
options.Scope.Add("openid"); options.Scope.Add("openid");
options.Scope.Add("profile"); options.Scope.Add("profile");
options.Scope.Add("role");
options.Scope.Add("email"); options.Scope.Add("email");
options.Events = new OpenIdConnectEvents options.Events = new OpenIdConnectEvents
@@ -151,7 +149,7 @@ public static class Security
}); });
services.AddAuthorizationBuilder() services.AddAuthorizationBuilder()
.AddPolicy(Constants.MainScope, policy => .AddPolicy("ApiScope", policy =>
{ {
policy.RequireAuthenticatedUser(); policy.RequireAuthenticatedUser();
policy.RequireClaim("scope", authOptions.Audience!); policy.RequireClaim("scope", authOptions.Audience!);
@@ -1,73 +0,0 @@
using PostFundManagement.Application.Abstractions;
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Services.Communications;
using PostFundManagement.Application.Services.Evidence;
using PostFundManagement.Application.Services.Execution;
using PostFundManagement.Application.Services.Governance;
using PostFundManagement.Application.Services.Grants;
using PostFundManagement.Application.Services.Projects;
using PostFundManagement.Application.Services.Shared;
using PostFundManagement.Application.Services.Tools;
using PostFundManagement.Application.Services.Users;
using PostFundManagement.Domain.Abstractions;
namespace PostFundManagement.Application.Extensions;
public static class Services
{
public static IServiceCollection AddRegisterServicesWithCaching(this IServiceCollection services, IConfiguration configuration)
{
services.AddScoped<ICommunicationsService, CommunicationsService>();
services.Decorate<ICommunicationsService, CommunicationsCacheService>();
services.AddScoped<IEvidenceService, EvidenceService>();
services.Decorate<IEvidenceService, EvidenceCacheService>();
services.AddScoped<IUserService, UserService>();
services.Decorate<IUserService, UserCacheService>();
services.AddScoped<IIdentityService, IdentityService>();
services.Decorate<IIdentityService, IdentityCacheService>();
services.AddScoped<IExecutionService, ExecutionService>();
services.Decorate<IExecutionService, ExecutionCacheService>();
services.AddScoped<IGovernanceService, GovernanceService>();
services.Decorate<IGovernanceService, GovernanceCacheService>();
services.AddScoped<IGrantsService, GrantsService>();
services.Decorate<IGrantsService, GrantsCacheService>();
services.AddScoped<IProjectService, ProjectService>();
services.Decorate<IProjectService, ProjectCacheService>();
services.AddScoped<IToolService, ToolService>();
services.Decorate<IToolService, ToolCacheService>();
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = configuration.GetConnectionString("Redis");
options.InstanceName = "PFM-API_";
});
services.AddHybridCache(options => options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(2),
LocalCacheExpiration = TimeSpan.FromMinutes(30)
});
return services;
}
public static IServiceCollection AddSharedServices(this IServiceCollection services)
{
var serviceType = typeof(IService);
var sharedImplementations = typeof(IApplication).Assembly.GetTypes()
.Where(t => serviceType.IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
foreach (var sharedImplementation in sharedImplementations) services.AddScoped(sharedImplementation);
return services;
}
}
@@ -1,10 +1,10 @@
using PostFundManagement.Application.Abstractions.Services; using PostFundManagement.Application.Services.Shared;
using static PostFundManagement.Application.Extensions.General; using static PostFundManagement.Application.Extensions.General;
using static PostFundManagement.Domain.Extensions.Constants; using static PostFundManagement.Domain.Extensions.Constants;
namespace PostFundManagement.Application.Filters; namespace PostFundManagement.Application.Filters;
public sealed class ActiveUserFilter(IIdentityService identityService) : IEndpointFilter public sealed class ActiveUserFilter(IdentityService identityService) : IEndpointFilter
{ {
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{ {

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