Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c88b9e533 | ||
|
|
7d67408d2c | ||
|
|
d1d58a6183 | ||
|
|
60ad154841 | ||
|
|
2c13170859 | ||
|
|
d87c8aa3c9 | ||
|
|
6af7815ad1 | ||
|
|
a169f6c3f6 | ||
|
|
65ea753c19 | ||
|
|
e6d2663265 | ||
|
|
91b5814b60 | ||
|
|
0c4c848568 | ||
|
|
fb05150ebf | ||
|
|
2f9c20020b | ||
|
|
a771957db3 | ||
|
|
46267a2a34 | ||
|
|
a7f360cc2a | ||
|
|
67c08b40ca |
@@ -14,9 +14,9 @@ public sealed class GetApplicantInviteEndpoint : IEndpoint
|
||||
{
|
||||
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, ICommunicationsService service,
|
||||
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),
|
||||
var r when r.HasError<NotFoundError>() => Results.NotFound(),
|
||||
|
||||
@@ -1,35 +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")
|
||||
.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();
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ public sealed class GetIndicatorEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Evidence)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Evidence);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ public class GetIndicatorsEndpoint : IEndpoint
|
||||
.Produces<Indicator[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Evidence)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Evidence);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public class GetProjectEvidenceEndpoint : IEndpoint
|
||||
{
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/evidence/{projectId}", async (string projectId, IEvidenceService service,
|
||||
builder.MapGet("api/evidence/milestone/{projectId}", async (string projectId, IEvidenceService service,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
await service.GetProjectEvidencesAsync(projectId, cancellationToken) switch
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Execution;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class ApproveDisbursementEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/execution/disbursements/{disbursementId}/approve", async (string disbursementId,
|
||||
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
ExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
await service.ApproveDisbursementAsync(disbursementId, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Execution;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateDisbursementEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/execution/disbursements", async (CreateDisbursementRequest request,
|
||||
IExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
ExecutionService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Execution;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -14,7 +13,7 @@ public class CreateRiskEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
|
||||
@@ -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.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public sealed class GetDisbursementEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetDisbursementAsync(disbursementIdHash: disbursementId, cancellationToken) switch
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public sealed class GetDisbursementEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status400BadRequest)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Execution)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Execution;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetDisbursementsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetDisbursementsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -28,7 +28,6 @@ public class GetDisbursementsEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Execution)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public sealed class GetRiskEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetRiskAsync(riskIdHash: riskId, cancellationToken) switch
|
||||
{
|
||||
@@ -30,7 +30,6 @@ public sealed class GetRiskEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Execution)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Execution;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -13,7 +13,7 @@ public class GetRisksEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetRisksAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -26,7 +26,6 @@ public class GetRisksEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Execution)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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.Errors;
|
||||
@@ -15,7 +14,7 @@ public sealed class UpdateDisbursementEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut("api/execution/disbursements", async (UpdateDisbursementRequest request, HttpContext context,
|
||||
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||
ExecutionService service, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateDisbursementAsync(request, userId: context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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.Errors;
|
||||
@@ -15,7 +14,7 @@ public sealed class UpdateRiskEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut("api/execution/risks", async (UpdateRiskRequest request,
|
||||
IExecutionService service, CancellationToken cancellationToken = default) =>
|
||||
ExecutionService service, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateRiskAsync(request, cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Governance;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public sealed class CreateOrganisationEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/governance/organisations", async (CreateOrganisationRequest request, HttpContext context,
|
||||
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||
GovernanceService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateOrganisationAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,38 +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")
|
||||
.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.Services.Governance;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -13,7 +12,7 @@ public class DashboardEndpoint : IEndpoint
|
||||
{
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/governance/dashboard", async (IGovernanceService service,
|
||||
builder.MapGet("api/governance/dashboard", async (GovernanceService service,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
await service.GetDashboardAsync(cancellationToken) switch
|
||||
{
|
||||
@@ -26,7 +25,6 @@ public class DashboardEndpoint : IEndpoint
|
||||
.Produces<Dashboard>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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.Models;
|
||||
using PostFundManagement.Domain.Security;
|
||||
@@ -14,22 +13,19 @@ public class GetInstrumentsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetInstrumentsAsync(programmeId, cancellationToken) switch
|
||||
await service.GetInstrumentsAsync(cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs),
|
||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||
var r => Results.InternalServerError(r.Errors.ToProblems()),
|
||||
})
|
||||
.WithDescription("Get all funding instruments")
|
||||
.WithName(typeof(GetInstrumentsEndpoint).ToEndpointName())
|
||||
.MapToApiVersion(new ApiVersion(1))
|
||||
.Produces<Instrument[]>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -15,10 +15,10 @@ public class GetOrganisationEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
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
|
||||
{
|
||||
{ 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<NotFoundError>() => Results.NotFound(),
|
||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||
@@ -31,7 +31,6 @@ public class GetOrganisationEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Governance;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetOrganisationsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetOrganisationsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -27,7 +27,6 @@ public class GetOrganisationsEndpoint : IEndpoint
|
||||
.Produces<Organisation[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.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")
|
||||
.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,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 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")
|
||||
.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.Services.Governance;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
using PostFundManagement.Domain.Security;
|
||||
@@ -14,22 +13,19 @@ public class GetPortfoliosEndpoint : IEndpoint
|
||||
{
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/governance/organisations/{organisationId}/portfolio", async (string organisationId,
|
||||
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||
await service.GetPorfoliosAsync(organisationId, cancellationToken) switch
|
||||
builder.MapGet("api/governance/portfolios", async (GovernanceService service,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
await service.GetPorfoliosAsync(cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios),
|
||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||
})
|
||||
.WithDescription("Get all portfolios")
|
||||
.WithName(typeof(GetPortfoliosEndpoint).ToEndpointName())
|
||||
.MapToApiVersion(new ApiVersion(1))
|
||||
.Produces<Portfolio[]>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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.Models;
|
||||
using PostFundManagement.Domain.Security;
|
||||
@@ -14,22 +13,19 @@ public class GetProgrammesEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetProgrammesAsync(portfolioId, cancellationToken) switch
|
||||
await service.GetProgrammesAsync(cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var programmes } => Results.Ok(programmes),
|
||||
var r when r.HasError<IdDecodeError>() => Results.Forbid(),
|
||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||
})
|
||||
.WithDescription("Get all programmes")
|
||||
.WithName(typeof(GetProgrammesEndpoint).ToEndpointName())
|
||||
.MapToApiVersion(new ApiVersion(1))
|
||||
.Produces<Programme[]>(StatusCodes.Status200OK)
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Governance)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Governance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
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.Errors;
|
||||
@@ -15,7 +14,7 @@ public sealed class UpdateOrganisationEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut("api/governance/organisations", async (UpdateOrganisationRequest request,
|
||||
IGovernanceService service, CancellationToken cancellationToken = default) =>
|
||||
GovernanceService service, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateOrganisationAsync(request, cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,38 +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")
|
||||
.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.Services.Grants;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateAwardEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/grants/awards", async (CreateAwardRequest request, HttpContext context,
|
||||
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||
GrantsService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateBudgetEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/grants/budgets", async (CreateBudgetRequest request, HttpContext context,
|
||||
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||
GrantsService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateBudgetAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateContractEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/grants/contracts", async (CreateContractRequest request, HttpContext context,
|
||||
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||
GrantsService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class ExecuteContractEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/grants/contracts/execute", async (ExecuteContractRequest request, HttpContext context,
|
||||
IGrantsService service, CancellationToken cancellationToken = default) =>
|
||||
GrantsService service, CancellationToken cancellationToken = default) =>
|
||||
await service.ExecuteContractAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetAwardEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetAwardAsync(awardId, cancellationToken) switch
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public class GetAwardEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetAwardsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetAwardsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -27,7 +27,6 @@ public class GetAwardsEndpoint : IEndpoint
|
||||
.Produces<Award[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetBudgetEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetBudgetAsync(budgetId, cancellationToken) switch
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public class GetBudgetEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetBudgetsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetBudgetsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -27,7 +27,6 @@ public class GetBudgetsEndpoint : IEndpoint
|
||||
.Produces<Budget[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetContractEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetContractAsync(contractId, cancellationToken) switch
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public class GetContractEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Grants;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetContractsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetContractsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -27,7 +27,6 @@ public class GetContractsEndpoint : IEndpoint
|
||||
.Produces<Contract[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Grants)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Grants);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Projects;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateActivityEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/projects/activities", async (CreateActivityRequest request,
|
||||
HttpContext context, IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
HttpContext context, ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } result => Results.Ok(),
|
||||
|
||||
@@ -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 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")
|
||||
.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.Services.Projects;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class CreateProjectEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPost("api/projects", async (CreateProjectRequest request, HttpContext context,
|
||||
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } result => Results.Ok(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetActivitiesEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/projects/{projectId}/activities", async (string projectId,
|
||||
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.GetActivitiesAsync(projectId, cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var activities } => Results.Ok(activities),
|
||||
@@ -28,7 +28,6 @@ public class GetActivitiesEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetActivityEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/projects/activities/{activityId}", async (string activityId,
|
||||
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.GetActivityAsync(activityId, cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var activity } => Results.Ok(activity),
|
||||
@@ -30,7 +30,6 @@ public class GetActivityEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetMilestoneEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetMilestoneAsync(milestoneId, cancellationToken) switch
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Projects;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,20 +14,20 @@ public class GetMilestonesEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetMilestonesAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
|
||||
var r => Results.InternalServerError(r.Errors.ToProblems())
|
||||
})
|
||||
.RequireAuthorization()
|
||||
.WithDescription("Get a list of all milestones")
|
||||
.WithName(typeof(GetMilestonesEndpoint).ToEndpointName())
|
||||
.MapToApiVersion(new ApiVersion(1))
|
||||
.Produces<Milestone[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.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")
|
||||
.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,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 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")
|
||||
.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,35 +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")
|
||||
.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.Services.Projects;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetProjectEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetProjectAsync(projectId, cancellationToken) switch
|
||||
{
|
||||
@@ -31,7 +31,6 @@ public class GetProjectEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -14,7 +14,7 @@ public class GetProjectIndicatorsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetProjectIndicatorsAsync(projectIdHash: projectId, cancellationToken) switch
|
||||
{
|
||||
@@ -30,7 +30,6 @@ public class GetProjectIndicatorsEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -15,7 +15,7 @@ public class GetProjectMilestonesEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapGet("api/projects/{projectId}/milestones", async (string projectId,
|
||||
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.GetProjectMilestonesAsync(projectId, cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true, Value: var milestones } => Results.Ok(milestones),
|
||||
@@ -30,7 +30,6 @@ public class GetProjectMilestonesEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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;
|
||||
@@ -15,7 +15,7 @@ public class GetProjectRisksEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
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
|
||||
{
|
||||
{ IsSuccess: true, Value: var risks } => Results.Ok(risks),
|
||||
@@ -30,7 +30,6 @@ public class GetProjectRisksEndpoint : IEndpoint
|
||||
.Produces(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.WithTags(EndpointTags.Projects);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Projects;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -14,7 +14,7 @@ public class GetProjectsEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.GetProjectsAsync(Pagination.Create(page, pageSize), cancellationToken) switch
|
||||
{
|
||||
@@ -27,7 +27,6 @@ public class GetProjectsEndpoint : IEndpoint
|
||||
.Produces<Project[]>(StatusCodes.Status200OK)
|
||||
.Produces<Problems>(StatusCodes.Status500InternalServerError)
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.WithTags(EndpointTags.Projects)
|
||||
.CacheOutput();
|
||||
.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.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
@@ -13,7 +12,7 @@ public sealed class UpdateMilestoneEndpoint : IEndpoint
|
||||
{
|
||||
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) =>
|
||||
await service.UpdateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
|
||||
@@ -1,36 +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")
|
||||
.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.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
@@ -15,7 +14,7 @@ public sealed class UpdateProjectEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut("api/projects", async (UpdateProjectRequest request,
|
||||
IProjectService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, HttpContext context, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateProjectAsync(request, userId: context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Projects;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
@@ -15,7 +14,7 @@ public class UpdateProjectStatusEndpoint : IEndpoint
|
||||
public void Map(IEndpointRouteBuilder builder)
|
||||
{
|
||||
builder.MapPut("api/projects/status", async (UpdateProjectStatusRequest request, HttpContext context,
|
||||
IProjectService service, CancellationToken cancellationToken = default) =>
|
||||
ProjectService service, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch
|
||||
{
|
||||
{ IsSuccess: true } => Results.Ok(),
|
||||
|
||||
@@ -1,35 +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")
|
||||
.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();
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,6 @@
|
||||
<PackageLicenseFile>..\LICENSE</PackageLicenseFile>
|
||||
</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)-->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||
|
||||
@@ -42,9 +42,6 @@ builder.Services.AddHealthChecks()
|
||||
|
||||
IEnumerable<string> identityScopes = ["openid", "profile", "roles", "email", "address"];
|
||||
|
||||
builder.Services.AddValidation()
|
||||
.AddEndpointValidation();
|
||||
|
||||
builder.Services.AddOpenApi(options =>
|
||||
{
|
||||
options.AddDocumentTransformer((document, context, cancellationToken) =>
|
||||
@@ -91,12 +88,6 @@ builder.Services.AddOpenApi(options =>
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddCors(options => options.AddPolicy("AllowFrontend", policy =>
|
||||
policy.WithOrigins("https://localhost:3000", "https://localhost:8076", "https://console.ifri.khongisa.co.za")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials()));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
||||
@@ -109,7 +100,6 @@ app.UseHsts();
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseCors("AllowFrontend");
|
||||
app.UseOutputCache();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
@@ -10,7 +10,5 @@ public interface ICommunicationsService
|
||||
|
||||
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);
|
||||
ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid userId, 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,28 +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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -6,8 +6,6 @@ namespace PostFundManagement.Application.Extensions;
|
||||
|
||||
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 =>
|
||||
|
||||
@@ -1,12 +1,55 @@
|
||||
namespace PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Communications;
|
||||
using PostFundManagement.Application.Services.Evidence;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Application.Services.Users;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
|
||||
namespace PostFundManagement.Application.Extensions;
|
||||
|
||||
public static class General
|
||||
{
|
||||
public static string Clean(this string input) => Patterns.SpaceRegex().Replace(input, string.Empty);
|
||||
public static IServiceCollection AddRegisterServicesWithCaching(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddScoped<ICommunicationsService, CommunicationsService>();
|
||||
services.Decorate<ICommunicationsService, CommunicationsCacheService>();
|
||||
|
||||
public static string ToJson<TValue>(this TValue obj) where TValue : class => JsonSerializer.Serialize(obj);
|
||||
services.AddScoped<IEvidenceService, EvidenceService>();
|
||||
services.Decorate<IEvidenceService, EvidenceCacheService>();
|
||||
|
||||
public static TValue FromJson<TValue>(this string json) where TValue : class => JsonSerializer.Deserialize<TValue>(json)!;
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
services.Decorate<IUserService, UserCacheService>();
|
||||
|
||||
services.AddScoped<IIdentityService, IdentityService>();
|
||||
services.Decorate<IIdentityService, IdentityCacheService>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static Guid GetUserId(this HttpContext context) => (Guid)context.Items[Domain.Extensions.Constants.UserIdItemKey]!;
|
||||
|
||||
@@ -34,6 +77,6 @@ public static class General
|
||||
{
|
||||
Detail = string.Join(',', errors.Select(e => e.Message)),
|
||||
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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,6 @@
|
||||
<PackageLicenseFile>..\LICENSE</PackageLicenseFile>
|
||||
</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>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PostFundManagement.Domain\PostFundManagement.Domain.csproj" />
|
||||
<ProjectReference Include="..\PostFundManagement.Infrastructure\PostFundManagement.Infrastructure.csproj" />
|
||||
@@ -25,7 +17,6 @@
|
||||
<!-- API Documentation -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Validation" Version="10.0.11" />
|
||||
<PackageReference Include="Scalar.AspNetCore" Version="2.17.2" />
|
||||
|
||||
<Using Include="Scalar.AspNetCore" />
|
||||
@@ -35,8 +26,6 @@
|
||||
|
||||
<!-- Shared Usings -->
|
||||
<ItemGroup>
|
||||
<Using Include="Humanizer" />
|
||||
<Using Include="System.ComponentModel.DataAnnotations" />
|
||||
<Using Include="Microsoft.Extensions.Caching.Distributed" />
|
||||
<Using Include="Microsoft.Extensions.Caching.Hybrid" />
|
||||
<Using Include="System.IdentityModel.Tokens.Jwt" />
|
||||
|
||||
+6
-58
@@ -1,74 +1,22 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Communications;
|
||||
|
||||
public sealed class CommunicationsCacheService(CommunicationsService service, IDistributedCache cache) : ServiceCacheBase, ICommunicationsService
|
||||
public sealed class CommunicationsCacheService(CommunicationsService service, HybridCache cache) : ServiceCacheBase, ICommunicationsService
|
||||
{
|
||||
public async ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Invite>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getapplicantinvite-{applicantUserId}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetApplicantInviteAsync(applicantUserId, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
}
|
||||
|
||||
return Result.Ok(cached!.FromJson<Invite>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Invite[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getapplicantinvites-{applicantUserId}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Invite[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getapplicantinvite-{userId}").Value,
|
||||
async cancel => await service.GetApplicantInviteAsync(userId, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Invite[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getinvites-{pagination.Page}-{pagination.PageSize}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetInvitesAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return Result.Ok(result.Value);
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Invite[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getinvites-{pagination.Page}-{pagination.PageSize}").Value,
|
||||
async cancel => await service.GetInvitesAsync(pagination, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.SendInviteAsync(request, userId, cancellationToken);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -38,8 +39,9 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New)
|
||||
.ToResult(invite.Map());
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(invite.Map())
|
||||
: Result.Fail<Invite>(CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -56,27 +58,15 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
return Result.Ok(invites.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid applicantUserId, CancellationToken cancellationToken) =>
|
||||
public async ValueTask<Result<Invite>> GetApplicantInviteAsync(Guid userId, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result<Invite>>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var invite = await context.Invites.AsNoTracking().FirstOrDefaultAsync(i => i.Recipient == applicantUserId, cancellationToken);
|
||||
var invite = await context.Invites.AsNoTracking().FirstOrDefaultAsync(i => i.Recipient == userId, cancellationToken);
|
||||
|
||||
return Result.FailIf(invite is null, NotFoundError.New)
|
||||
.ToResult(invite!.Map());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Invite[]>>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var invites = await context.Invites.AsNoTracking()
|
||||
.Where(i => i.Recipient == applicantUserId)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(invites.Select(i => i.Map()).ToArray());
|
||||
return invite is not null
|
||||
? Result.Ok(invite.Map())
|
||||
: Result.Fail<Invite>(NotFoundError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Communications;
|
||||
|
||||
public record InviteRequest([Required] long OrganisationId,
|
||||
[Required] long InvitedOrganisationId,
|
||||
[Required] long AwardId,
|
||||
[Required] long ContractId,
|
||||
[Required] Guid Recipient,
|
||||
[Required][MaxLength(LabelLength)] string Message,
|
||||
[Required] NotificationPlatform Platform);
|
||||
public record InviteRequest(long OrganisationId, long InvitedOrganisationId, long AwardId, long ContractId, Guid Recipient, string Message, NotificationPlatform Platform);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Evidence;
|
||||
|
||||
public sealed class EvidenceCacheService(EvidenceService service, IDistributedCache cache) : ServiceCacheBase, IEvidenceService
|
||||
public sealed class EvidenceCacheService(EvidenceService service, HybridCache cache) : ServiceCacheBase, IEvidenceService
|
||||
{
|
||||
public async ValueTask<Result> CreateEvidenceAsync(Guid userId, IFormCollection form, IFormFile file, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateEvidenceAsync(userId, form, file, cancellationToken);
|
||||
@@ -20,129 +19,33 @@ public sealed class EvidenceCacheService(EvidenceService service, IDistributedCa
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Evidence>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getevidence-{evidenceId}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetEvidenceAsync(evidenceId, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Evidence>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getevidence-{evidenceId}").Value,
|
||||
async cancel => await service.GetEvidenceAsync(evidenceId, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Evidence[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getevidencefiles-{pagination.Page}-{pagination.PageSize}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetEvidenceFilesAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Evidence[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getevidencefiles-{pagination.Page}-{pagination.PageSize}").Value,
|
||||
async cancel => await service.GetEvidenceFilesAsync(pagination, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Indicator>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getindicator-{indicatorId}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetIndicatorAsync(indicatorId, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Indicator>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getindicator-{indicatorId}").Value,
|
||||
async cancel => await service.GetIndicatorAsync(indicatorId, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Indicator[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getindicators-{pagination.Page}-{pagination.PageSize}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetIndicatorsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Indicator[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getindicators-{pagination.Page}-{pagination.PageSize}").Value,
|
||||
async cancel => await service.GetIndicatorsAsync(pagination, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Evidence[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getmilestoneevidences-{milestoneIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetMilestoneEvidencesAsync(milestoneIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Evidence[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getmilestoneevidences-{milestoneIdHash}").Value,
|
||||
async cancel => await service.GetMilestoneEvidencesAsync(milestoneIdHash, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Evidence[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprojectevidences-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectEvidencesAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Evidence[]>());
|
||||
}));
|
||||
await cache.GetOrCreateAsync(HashService.ToMd5Hash($"getprojectevidences-{projectIdHash}").Value,
|
||||
async cancel => await service.GetProjectEvidencesAsync(projectIdHash, cancellationToken))));
|
||||
|
||||
public async ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.RecordIndicatorActualAmountAsync(indicatorIdHash, amount, userId, cancellationToken);
|
||||
|
||||
@@ -47,7 +47,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
|
||||
context.Evidences.Add(evidence);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -66,7 +66,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
.SetProperty(f => f.ActualAmount, amount)
|
||||
.SetProperty(f => f.CreatedBy, userId), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -102,9 +102,6 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
if (!await context.Projects.AnyAsync(p => p.Id == request.ProjectId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
@@ -119,8 +116,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0,
|
||||
CreateError.Create("No record could be created."));
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateIndicatorAsync(CreateIndicatorRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -143,7 +139,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) =>
|
||||
@@ -159,8 +155,9 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
|
||||
var indicator = await context.Indicators.AsNoTracking().FirstOrDefaultAsync(i => i.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(indicator is null, NotFoundError.New)
|
||||
.ToResult(indicator!.Map());
|
||||
return indicator is not null
|
||||
? Result.Ok(indicator.Map())
|
||||
: Result.Fail(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) =>
|
||||
@@ -176,8 +173,9 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
|
||||
var evidence = await context.Evidences.AsNoTracking().FirstOrDefaultAsync(e => e.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(evidence is null, NotFoundError.New)
|
||||
.ToResult(evidence!.Map());
|
||||
return evidence is not null
|
||||
? Result.Ok(evidence.Map())
|
||||
: Result.Fail<Domain.Models.Evidence>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -236,6 +234,6 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
.SetProperty(f => f.UpdatedBy, userId)
|
||||
.SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Evidence;
|
||||
|
||||
public record UpdateEvidenceRequest([Required] long EvidenceId,
|
||||
[Required] int Version,
|
||||
[Required] EvidenceType EvidenceType,
|
||||
[Required] ApprovalStatus Status);
|
||||
public record UpdateEvidenceRequest(long EvidenceId, int Version, EvidenceType EvidenceType, ApprovalStatus Status);
|
||||
|
||||
public record RecordActualRequest([Required] decimal ActualAmount);
|
||||
public record RecordActualRequest(decimal ActualAmount);
|
||||
|
||||
public record CreateMilestoneRequest([Required] long ProjectId,
|
||||
[Required] DateTime DueAt,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] Priority Priority);
|
||||
public record CreateMilestoneRequest(long ProjectId, DateTime DueAt, string Name, Priority Priority);
|
||||
|
||||
public record CreateIndicatorRequest([Required] long ProjectId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] UnitOfMeasure UnitOfMeasure,
|
||||
[Required] decimal BaselineAmount,
|
||||
[Required] decimal TargetAmount);
|
||||
public record CreateIndicatorRequest(long ProjectId, string Name, UnitOfMeasure UnitOfMeasure, decimal BaselineAmount, decimal TargetAmount);
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Execution;
|
||||
|
||||
public sealed class ExecutionCacheService(ExecutionService service, IDistributedCache cache) : ServiceCacheBase, IExecutionService
|
||||
{
|
||||
public async ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.ApproveDisbursementAsync(disbursementIdHash, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateDisbursementAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateRiskAsync(CreateRiskRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateRiskAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Disbursement>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getdisbursement-{disbursementIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetDisbursementAsync(disbursementIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Disbursement>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Disbursement[]>> GetDisbursementsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Disbursement[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getdisbursements-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetDisbursementsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Disbursement[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Risk>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getrisk-{riskIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetRiskAsync(riskIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Risk>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Risk[]>> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Risk[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getrisks-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetRisksAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Risk[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateDisbursementAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateRiskAsync(request, cancellationToken);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -8,7 +8,7 @@ using PostFundManagement.Infrastructure.Database;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Execution;
|
||||
|
||||
public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IExecutionService
|
||||
public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IService
|
||||
{
|
||||
public async ValueTask<Result<Risk[]>> GetRisksAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Risk[]>>>)(async () =>
|
||||
@@ -57,7 +57,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -78,7 +78,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -98,7 +98,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(f => f.UpdatedBy, userId)
|
||||
.SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken) =>
|
||||
@@ -115,11 +115,12 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
var disbursement = await context.Disbursements.AsNoTracking()
|
||||
.FirstOrDefaultAsync(d => d.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(disbursement is null, NotFoundError.New)
|
||||
.ToResult(disbursement!.Map());
|
||||
return disbursement is not null
|
||||
? Result.Ok(disbursement.Map())
|
||||
: Result.Fail<Disbursement>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result<Risk>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(riskIdHash);
|
||||
@@ -133,11 +134,12 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
var risk = await context.Risks.AsNoTracking()
|
||||
.FirstOrDefaultAsync(r => r.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(risk is null, NotFoundError.New)
|
||||
.ToResult(risk!.Map());
|
||||
return risk is not null
|
||||
? Result.Ok(risk.Map())
|
||||
: Result.Fail<Risk>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -150,10 +152,10 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(u => u.Likelihood, request.Likelihood)
|
||||
.SetProperty(u => u.Impact, request.Impact), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -166,6 +168,6 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(d => d.UpdatedAt, DateTime.UtcNow)
|
||||
.SetProperty(d => d.UpdatedBy, userId), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Execution;
|
||||
|
||||
public record UpdateDisbursementRequest([Required] long DisbursementId, long? MilestoneId, [Required] decimal Amount);
|
||||
public record UpdateDisbursementRequest(long DisbursementId, long? MilestoneId, decimal Amount);
|
||||
|
||||
public record UpdateRiskRequest([Required] long RiskId,
|
||||
[Required] RiskLikelihood Likelihood,
|
||||
[Required] RiskImpact Impact,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description);
|
||||
public record UpdateRiskRequest(long RiskId, RiskLikelihood Likelihood, RiskImpact Impact, string Name, string Description);
|
||||
|
||||
public record CreateDisbursementRequest([Required] long ProjectId, long? MilestoneId, [Required] decimal Amount);
|
||||
public record CreateDisbursementRequest(long ProjectId, long? MilestoneId, decimal Amount);
|
||||
|
||||
public record CreateRiskRequest([Required] long ProjectId,
|
||||
[Required] RiskLikelihood Likelihood,
|
||||
[Required] RiskImpact Impact,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description);
|
||||
public record CreateRiskRequest(long ProjectId, RiskLikelihood Likelihood, RiskImpact Impact, string Name, string Description);
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Governance;
|
||||
|
||||
public sealed class GovernanceCacheService(GovernanceService service, IDistributedCache cache) : ServiceCacheBase, IGovernanceService
|
||||
{
|
||||
public async ValueTask<Result> CreateOrganisationAsync(CreateOrganisationRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateOrganisationAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreatePortfolioAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Dashboard>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getdashboard").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetDashboardAsync(cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Dashboard>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Instrument[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getinstruments").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetInstrumentsAsync(programmeIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Instrument[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Organisation>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getorganisation-{organisationIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetOrganisationAsync(organisationIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Organisation>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Organisation[]>> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Organisation[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getorganisations-{pagination.Page}-{pagination.PageSize}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetOrganisationsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Organisation[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Portfolio[]>> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Portfolio[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getportfolios-{organisationIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetPorfoliosAsync(organisationIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Portfolio[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Portfolio>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getportfolio-{portfolioIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetPortfolioAsync(portfolioIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Portfolio>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getportfolioprogrammes-{portfolioIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetPortfolioProgrammesAsync(portfolioIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Programme[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme[]>> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprogrammes-{portfolioIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProgrammesAsync(portfolioIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Programme[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateOrganisationAsync(request, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdatePortfolioAsync(request, userId, cancellationToken);
|
||||
}
|
||||
@@ -1,50 +1,31 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
using PostFundManagement.Infrastructure.Database;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Governance;
|
||||
|
||||
public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IGovernanceService
|
||||
public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IService
|
||||
{
|
||||
public async ValueTask<Result<Programme[]>> GetProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Programme[]>> GetProgrammesAsync(CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var programmes = await context.Programmes.AsNoTracking()
|
||||
.Where(p => p.PortfolioId == decodeResult.Value)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
var programmes = await context.Programmes.AsNoTracking().ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(programmes.Select(p => p.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Portfolio[]>> GetPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Portfolio[]>> GetPorfoliosAsync(CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Portfolio[]>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(organisationIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var portfolios = await context.Portfolios.AsNoTracking()
|
||||
.Where(f => f.Id == decodeResult.Value)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
var portfolios = await context.Portfolios.AsNoTracking().ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(portfolios.Select(p => p.Map()).ToArray());
|
||||
}));
|
||||
@@ -77,25 +58,17 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> co
|
||||
var organisation = await context.Organisations.AsNoTracking()
|
||||
.FirstOrDefaultAsync(o => o.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(organisation is null, NotFoundError.New)
|
||||
.ToResult(organisation!.Map());
|
||||
return organisation is not null
|
||||
? Result.Ok(organisation.Map())
|
||||
: Result.Fail(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Instrument[]>> GetInstrumentsAsync(CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Instrument[]>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(programmeIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var instruments = await context.Instruments.AsNoTracking()
|
||||
.Where(i => i.ProgrammeId == decodeResult.Value)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
var instruments = await context.Instruments.AsNoTracking().ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(instruments.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
@@ -131,23 +104,21 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> co
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var registrationNo = request.RegistrationNo.Clean();
|
||||
|
||||
if (await context.Organisations.AnyAsync(o => o.RegistrationNo == registrationNo, cancellationToken))
|
||||
if (await context.Organisations.AnyAsync(o => o.RegistrationNo == request.RegistrationNo, cancellationToken))
|
||||
return Result.Fail(CreateError.Create("Organisation with the same registration number already exists"));
|
||||
|
||||
context.Organisations.Add(new Domain.Entities.Organisation
|
||||
{
|
||||
Name = request.Name.Humanize(LetterCasing.Title).Trim(),
|
||||
RegistrationNo = registrationNo,
|
||||
Email = request.Email.ToLower(CultureInfo.InvariantCulture).Clean(),
|
||||
Name = request.Name,
|
||||
RegistrationNo = request.RegistrationNo,
|
||||
Email = request.Email,
|
||||
Type = request.Type,
|
||||
Status = ActivityStatus.Active,
|
||||
CreatedBy = userId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Organisation>> UpdateOrganisationAsync(UpdateOrganisationRequest request, CancellationToken cancellationToken = default) =>
|
||||
@@ -158,95 +129,11 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> co
|
||||
var changedTotal = await context.Organisations
|
||||
.Where(f => f.Id == request.OrganisationId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Name, request.Name.Humanize(LetterCasing.Title).Trim())
|
||||
.SetProperty(u => u.RegistrationNo, request.RegistrationNo.Clean())
|
||||
.SetProperty(u => u.Email, request.Email.ToLower(CultureInfo.InvariantCulture).Clean())
|
||||
.SetProperty(u => u.Name, request.Name)
|
||||
.SetProperty(u => u.RegistrationNo, request.RegistrationNo)
|
||||
.SetProperty(u => u.Email, request.Email)
|
||||
.SetProperty(u => u.Type, request.Type), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreatePortfolioAsync(CreatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == request.OwnerId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Owner user not found"));
|
||||
|
||||
if (!await context.Organisations.AnyAsync(u => u.Id == request.OrganisationId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Organisation not found"));
|
||||
|
||||
var name = request.Name.Humanize(LetterCasing.Title).Trim();
|
||||
|
||||
if (await context.Portfolios.AnyAsync(p => p.Name == name && p.OrganisationId == request.OrganisationId, cancellationToken))
|
||||
return Result.Fail(ValidationError.Create("A portfolio with the same name exists in this organisation"));
|
||||
|
||||
context.Portfolios.Add(new Domain.Entities.Portfolio
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CreatedBy = userId,
|
||||
OrganisationId = request.OrganisationId,
|
||||
OwnedBy = request.OwnerId,
|
||||
Name = name,
|
||||
Description = request.Description.Humanize(LetterCasing.Sentence).Trim()
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == request.OwnerId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.Create("Owner user not found"));
|
||||
|
||||
var changedTotal = await context.Portfolios
|
||||
.Where(p => p.Id == request.PortfolioId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Name, request.Name.Humanize(LetterCasing.Title).Trim())
|
||||
.SetProperty(u => u.OwnedBy, request.OwnerId)
|
||||
.SetProperty(u => u.Description, request.Description.Humanize(LetterCasing.Sentence).Trim()), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Nothing was saved, no change detected."));
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme[]>> GetPortfolioProgrammesAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var programmes = await context.Programmes.AsNoTracking()
|
||||
.Where(f => f.PortfolioId == decodeResult.Value)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(programmes.Select(p => p.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Portfolio>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(portfolioIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var portfolio = await context.Portfolios.AsNoTracking()
|
||||
.FirstOrDefaultAsync(p => p.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(portfolio is null, NotFoundError.New)
|
||||
.ToResult(portfolio!.Map());
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,32 +1,9 @@
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Governance;
|
||||
|
||||
public record UpdatePortfolioRequest([Required] long PortfolioId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description,
|
||||
[Required] Guid OwnerId);
|
||||
public record UpdateOrganisationRequest(long OrganisationId, string Name, string RegistrationNo, string Email, OrganisationType Type);
|
||||
|
||||
public record CreatePortfolioRequest([Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description,
|
||||
[Required] long OrganisationId,
|
||||
[Required] Guid OwnerId);
|
||||
public record CreateOrganisationRequest(string Name, string RegistrationNo, string Email, OrganisationType Type);
|
||||
|
||||
public record UpdateOrganisationRequest([Required] long OrganisationId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(LabelLength)] string RegistrationNo,
|
||||
[Required][EmailAddress][MaxLength(LabelLength)] string Email,
|
||||
[Required] OrganisationType Type);
|
||||
|
||||
public record CreateOrganisationRequest([Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(LabelLength)] string RegistrationNo,
|
||||
[Required][EmailAddress][MaxLength(LabelLength)] string Email,
|
||||
[Required] OrganisationType Type = OrganisationType.PrivateLimitedCompany);
|
||||
|
||||
public record Dashboard([Required] decimal PortfolioValue,
|
||||
[Required] int ActiveAwards,
|
||||
[Required] decimal Disbursed,
|
||||
[Required] double OnTrackPercentage,
|
||||
[Required] int AtRiskCount,
|
||||
[Required] int BeneficiariesReached);
|
||||
public record Dashboard(decimal PortfolioValue, int ActiveAwards, decimal Disbursed, double OnTrackPercentage, int AtRiskCount, int BeneficiariesReached);
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
public sealed class GrantsCacheService(GrantsService service, IDistributedCache cache) : ServiceCacheBase, IGrantsService
|
||||
{
|
||||
public async ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateAwardAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateBudgetAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateContractAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.ExecuteContractAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Award>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getaward-{awardIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetAwardAsync(awardIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Award>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Award[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getawards-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetAwardsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Award[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Budget>> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Budget>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getbudget-{budgetIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetBudgetAsync(budgetIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Budget>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Budget[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getbudgets-{pagination.Page}-{pagination.PageSize}-{pagination.Index}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetBudgetsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Budget[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Contract>> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Contract>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getctract-{contractIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetContractAsync(contractIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Contract>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Contract[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getcontracts-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetContractsAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Contract[]>());
|
||||
}));
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -8,7 +8,7 @@ using PostFundManagement.Infrastructure.Database;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IGrantsService
|
||||
public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IService
|
||||
{
|
||||
public async ValueTask<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Contract[]>>>)(async () =>
|
||||
@@ -38,8 +38,9 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
var contract = await context.Contracts.AsNoTracking()
|
||||
.FirstOrDefaultAsync(c => c.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(contract is null, NotFoundError.New)
|
||||
.ToResult(contract!.Map());
|
||||
return contract is not null
|
||||
? Result.Ok(contract.Map())
|
||||
: Result.Fail<Contract>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -70,8 +71,9 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
var budget = await context.Budgets.AsNoTracking()
|
||||
.FirstOrDefaultAsync(b => b.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(budget is null, NotFoundError.New)
|
||||
.ToResult(budget!.Map());
|
||||
return budget is not null
|
||||
? Result.Ok(budget.Map())
|
||||
: Result.Fail<Budget>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -101,8 +103,9 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
var award = await context.Awards.AsNoTracking().FirstOrDefaultAsync(a => a.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(award is null, NotFoundError.New)
|
||||
.ToResult(award!.Map());
|
||||
return award is not null
|
||||
? Result.Ok(award.Map())
|
||||
: Result.Fail<Award>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -118,7 +121,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
.SetProperty(u => u.CreatedBy, userId)
|
||||
.SetProperty(u => u.Status, ContractStatus.Active), cancellationToken);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -145,7 +148,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
context.Contracts.Add(contract);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -170,7 +173,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
context.Budgets.Add(budget);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -199,6 +202,6 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
context.Awards.Add(award);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
public record CreateAwardRequest(long OrganisationId, long ProgrammeId, decimal Amount);
|
||||
|
||||
public record CreateAwardRequest([Required] long OrganisationId, [Required] long ProgrammeId, [Required] decimal Amount);
|
||||
public record CreateBudgetRequest(long ProjectId, string Name, decimal Amount);
|
||||
|
||||
public record CreateBudgetRequest([Required] long ProjectId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] decimal Amount);
|
||||
public record CreateContractRequest(long AwardId, decimal TotalValue, DateTime EffectiveAt, DateTime ExpiresAt);
|
||||
|
||||
public record CreateContractRequest([Required] long AwardId,
|
||||
[Required] decimal TotalValue,
|
||||
[Required] DateTime EffectiveAt,
|
||||
[Required] DateTime ExpiresAt);
|
||||
|
||||
public record ExecuteContractRequest([Required] long ContractId,
|
||||
[Required][MaxLength(LabelLength)] string ExternalSignatureId,
|
||||
[Required][MaxLength(LabelLength)] string SignedDocumentUrl);
|
||||
public record ExecuteContractRequest(long ContractId, string ExternalSignatureId, string SignedDocumentUrl);
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Projects;
|
||||
|
||||
public sealed class ProjectCacheService(ProjectService service, IDistributedCache cache) : ServiceCacheBase, IProjectService
|
||||
{
|
||||
public async ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateActivityAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateProgrammeAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.CreateProjectAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Activity[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getactivities-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetActivitiesAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Activity[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Activity>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getactivity-{activityIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetActivityAsync(activityIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Domain.Models.Activity>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Milestone>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getmilestone-{milestoneIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetMilestoneAsync(milestoneIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Milestone>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Milestone[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getmilestones-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetMilestonesAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Milestone[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprogramme-{programmeIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProgrammeAsync(programmeIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached!.FromJson<Programme>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Project[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprogrammeprojects-{programmeIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProgrammeProjectsAsync(programmeIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Project[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprogrammes-{pagination.Index}-{pagination.PageSize}-{pagination.Page}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProgrammesAsync(pagination, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Programme[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Project>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getproject-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Project>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Indicator[]>> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Indicator[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprojectindicators-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectIndicatorsAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if(result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Indicator[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Milestone[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprojectmilestones-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectMilestonesAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if(result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Milestone[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Risk[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprojectrisks-{projectIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectRisksAsync(projectIdHash, cancellationToken);
|
||||
|
||||
if(result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Risk[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Project[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getprojects-{pagination.Index}-{pagination.Page}-{pagination.PageSize}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetProjectsAsync(pagination, cancellationToken);
|
||||
|
||||
if(result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<Project[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateMilestoneAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateProgrammeAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateProjectAsync(request, userId, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateProjectStatusAsync(request, userId, cancellationToken);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -8,24 +8,32 @@ using PostFundManagement.Infrastructure.Database;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Projects;
|
||||
|
||||
public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IProjectService
|
||||
public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IService
|
||||
{
|
||||
public async ValueTask<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken))
|
||||
var project = await context.Projects.FirstOrDefaultAsync(p => p.Id == request.ProjectId, cancellationToken);
|
||||
|
||||
if (project == null)
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
project.Status = request.Status;
|
||||
project.UpdatedBy = userId;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
context.Projects.Update(project);
|
||||
|
||||
var changedTotal = await context.Projects
|
||||
.Where(f => f.Id == request.ProjectId)
|
||||
.Where(p => p.Id != request.ProjectId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(p => p.Status, request.Status)
|
||||
.SetProperty(p => p.UpdatedBy, userId)
|
||||
.SetProperty(p => p.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled."));
|
||||
return Result.FailIf(changedTotal == 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Project[]>> GetProjectsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -55,8 +63,9 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
var project = await context.Projects.AsNoTracking()
|
||||
.FirstOrDefaultAsync(p => p.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(project is null, NotFoundError.New)
|
||||
.ToResult(project!.Map());
|
||||
return project is not null
|
||||
? Result.Ok(project.Map())
|
||||
: Result.Fail<Project>(NotFoundError.Create());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -82,21 +91,17 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var name = request.Name.Humanize(LetterCasing.Title).Trim();
|
||||
|
||||
if (await context.Projects.AnyAsync(p => p.Name == name && p.Sector == request.Sector && p.ProjectType == request.ProjectType, cancellationToken))
|
||||
if (await context.Projects.AnyAsync(p => p.Name == request.Name && p.Sector == request.Sector && p.ProjectType == request.ProjectType, cancellationToken))
|
||||
return Result.Fail(ValidationError.Create("A similarly named project exists of the same project type and sector"));
|
||||
|
||||
if (!await context.Programmes.AnyAsync(p => p.Id == request.ProgrammeId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
var description = request.Description.Humanize(LetterCasing.Sentence).Trim();
|
||||
|
||||
context.Projects.Add(new Domain.Entities.Project
|
||||
{
|
||||
ProgrammeId = request.ProgrammeId,
|
||||
Name = name,
|
||||
Description = description,
|
||||
Name = request.Name,
|
||||
Description = request.Description,
|
||||
Sector = request.Sector,
|
||||
ProjectType = request.ProjectType,
|
||||
Status = ApprovalStatus.Pending,
|
||||
@@ -104,8 +109,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0,
|
||||
CreateError.Create("No changes detected, update cancelled."));
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateActivityAsync(CreateActivityRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -124,9 +128,9 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
{
|
||||
ProjectId = request.ProjectId,
|
||||
MilestoneId = request.MilestoneId,
|
||||
Category = request.Category.Humanize(LetterCasing.Title).Trim(),
|
||||
Category = request.Category,
|
||||
Amount = request.Amount,
|
||||
Title = request.Title.Humanize(LetterCasing.Title).Trim(),
|
||||
Title = request.Title,
|
||||
Description = request.Description,
|
||||
PerformedAt = request.PerformedAt,
|
||||
Status = ApprovalStatus.Pending,
|
||||
@@ -134,8 +138,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0,
|
||||
CreateError.Create("No record was created."));
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone[]>> GetProjectMilestonesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -176,7 +179,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
return Result.Ok(evidences.Select(e => e.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result<Milestone>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(milestoneIdHash);
|
||||
@@ -189,8 +192,9 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
var milestone = await context.Milestones.AsNoTracking()
|
||||
.FirstOrDefaultAsync(m => m.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(milestone is null, NotFoundError.New)
|
||||
.ToResult(milestone!.Map());
|
||||
return milestone is not null
|
||||
? Result.Ok(milestone.Map())
|
||||
: Result.Fail<Milestone>(NotFoundError.Create());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -207,35 +211,32 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
return Result.Ok(milestones.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
var changedTotal = await context.Milestones.Where(m => m.Id == request.MilestoneId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Name, request.Name.Humanize(LetterCasing.Title).Trim())
|
||||
.SetProperty(u => u.Name, request.Name)
|
||||
.SetProperty(u => u.DueAt, request.DueAt)
|
||||
.SetProperty(u => u.Priority, request.Priority)
|
||||
.SetProperty(u => u.Status, request.Status)
|
||||
.SetProperty(u => u.UpdatedBy, userId)
|
||||
.SetProperty(u => u.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled."));
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result> UpdateProjectAsync(UpdateProjectRequest request, Guid userId, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var changedTotal = await context.Projects.Where(p => p.Id == request.ProjectId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Name, request.Name.Humanize(LetterCasing.Title).Trim())
|
||||
.SetProperty(u => u.Description, request.Description.Humanize(LetterCasing.Sentence).Trim())
|
||||
.SetProperty(u => u.Name, request.Name)
|
||||
.SetProperty(u => u.Description, request.Description)
|
||||
.SetProperty(u => u.StartedAt, request.StartedAt)
|
||||
.SetProperty(u => u.EndedAt, request.EndedAt)
|
||||
.SetProperty(u => u.UpdatedBy, userId)
|
||||
@@ -243,10 +244,10 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
.SetProperty(u => u.Sector, request.Sector)
|
||||
.SetProperty(u => u.ProjectType, request.ProjectType), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled."));
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken = default) =>
|
||||
public async ValueTask<Result<Domain.Models.Activity>> GetActivityAsync(string activityIdHash, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result<Domain.Models.Activity>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(activityIdHash);
|
||||
@@ -260,8 +261,9 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
var activity = await context.Activities.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(activity is null, NotFoundError.New)
|
||||
.ToResult(activity!.Map());
|
||||
return activity is not null
|
||||
? Result.Ok(activity.Map())
|
||||
: Result.Fail<Domain.Models.Activity>(NotFoundError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -282,100 +284,4 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
|
||||
return Result.Ok(risks.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var name = request.Name.Humanize(LetterCasing.Title).Trim();
|
||||
|
||||
if (!await context.Portfolios.AnyAsync(p => p.Id == request.PortfolioId, cancellationToken))
|
||||
return Result.Fail(ValidationError.Create("Portfolio does not exist."));
|
||||
|
||||
if (await context.Programmes.AnyAsync(p => p.Name == name, cancellationToken))
|
||||
return Result.Fail(ValidationError.Create("Programme of the same name exists"));
|
||||
|
||||
context.Programmes.Add(new Domain.Entities.Programme
|
||||
{
|
||||
PortfolioId = request.PortfolioId,
|
||||
OwnedBy = request.OwnerId,
|
||||
Name = name,
|
||||
CreatedBy = userId,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Status = ApprovalStatus.Pending
|
||||
});
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
var changedTotal = await context.Programmes.Where(p => p.Id == request.ProgrammeId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Name, request.Name.Humanize(LetterCasing.Title).Trim())
|
||||
.SetProperty(u => u.OwnedBy, request.OwnerId)
|
||||
.SetProperty(u => u.Status, request.Status)
|
||||
.SetProperty(u => u.UpdatedBy, userId)
|
||||
.SetProperty(u => u.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled."));
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme>> GetProgrammeAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(programmeIdHash);
|
||||
|
||||
if (decodeResult.IsFailed) return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var programme = await context.Programmes.FirstOrDefaultAsync(p => p.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(programme is null, NotFoundError.New)
|
||||
.ToResult(programme!.Map());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Programme[]>>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var programmes = await context.Programmes.AsNoTracking()
|
||||
.OrderByDescending(m => m.CreatedAt)
|
||||
.Skip((pagination.Page - 1) * pagination.PageSize)
|
||||
.Take(pagination.PageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(programmes.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Project[]>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(programmeIdHash);
|
||||
|
||||
if (decodeResult.IsFailed) return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Programmes.AnyAsync(p => p.Id == decodeResult.Value, cancellationToken))
|
||||
return Result.Fail(NotFoundError.New);
|
||||
|
||||
var projects = await context.Projects.AsNoTracking()
|
||||
.Where(p => p.ProgrammeId == decodeResult.Value)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return Result.Ok(projects.Select(i => i.Map()).ToArray());
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,40 +1,13 @@
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Projects;
|
||||
|
||||
public record UpdateProgrammeRequest([Required] long ProgrammeId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] Guid OwnerId,
|
||||
[Required] ApprovalStatus Status);
|
||||
public record UpdateProjectRequest(long ProjectId, string Name, string Description, DateTime StartedAt, DateTime? EndedAt, IndustrySector Sector, ProjectType ProjectType);
|
||||
|
||||
public record CreateProgrammeRequest(long PortfolioId, string Name, Guid OwnerId);
|
||||
public record UpdateMilestoneRequest(long MilestoneId, DateTime DueAt, string Name, Priority Priority, ApprovalStatus Status);
|
||||
|
||||
public record UpdateProjectRequest([Required] long ProjectId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description,
|
||||
DateTime? StartedAt, DateTime? EndedAt,
|
||||
[Required] IndustrySector Sector,
|
||||
[Required] ProjectType ProjectType);
|
||||
public record UpdateProjectStatusRequest(long ProjectId, ApprovalStatus Status);
|
||||
|
||||
public record UpdateMilestoneRequest([Required] long MilestoneId,
|
||||
[Required] DateTime DueAt,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] Priority Priority,
|
||||
[Required] ApprovalStatus Status);
|
||||
public record CreateProjectRequest(long ProgrammeId, string Name, string Description, IndustrySector Sector, ProjectType ProjectType);
|
||||
|
||||
public record UpdateProjectStatusRequest([Required] long ProjectId, [Required] ApprovalStatus Status);
|
||||
|
||||
public record CreateProjectRequest([Required] long ProgrammeId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required][MaxLength(TextLength)] string Description,
|
||||
[Required] IndustrySector Sector,
|
||||
[Required] ProjectType ProjectType);
|
||||
|
||||
public record CreateActivityRequest([Required] long ProjectId,
|
||||
[Required] long? MilestoneId,
|
||||
[Required][MaxLength(LabelLength)] string Category,
|
||||
[Required] decimal Amount,
|
||||
[Required][MaxLength(LabelLength)] string Title,
|
||||
[Required][MaxLength(TextLength)] string Description,
|
||||
DateTime? PerformedAt);
|
||||
public record CreateActivityRequest(long ProjectId, long? MilestoneId, string Category, decimal Amount, string Title, string Description, DateTime? PerformedAt);
|
||||
|
||||
@@ -33,9 +33,9 @@ public sealed class BrowserLocalStorageService(ProtectedLocalStorage storage)
|
||||
{
|
||||
var retrieval = await storage.GetAsync<string>(key);
|
||||
|
||||
return Result.FailIf(!retrieval.Success && string.IsNullOrWhiteSpace(retrieval.Value),
|
||||
NotFoundError.Create($"Could not find object by key {key}"))
|
||||
.ToResult(retrieval.Value!);
|
||||
return retrieval.Success && !string.IsNullOrWhiteSpace(retrieval.Value)
|
||||
? Result.Ok(retrieval.Value)
|
||||
: Result.Fail(NotFoundError.Create($"Could not find object by key {key}"));
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<TValue>> GetAsync<TValue>(string key) where TValue : class =>
|
||||
@@ -43,8 +43,8 @@ public sealed class BrowserLocalStorageService(ProtectedLocalStorage storage)
|
||||
{
|
||||
var retrieval = await storage.GetAsync<TValue>(key);
|
||||
|
||||
return Result.FailIf(!retrieval.Success && retrieval.Value is null,
|
||||
NotFoundError.Create($"Could not find object by key {key}"))
|
||||
.ToResult(retrieval.Value!);
|
||||
return retrieval.Success && retrieval.Value is not null
|
||||
? Result.Ok(retrieval.Value)
|
||||
: Result.Fail(NotFoundError.Create($"Could not find object by key {key}"));
|
||||
}));
|
||||
}
|
||||
@@ -1,16 +1,24 @@
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Shared;
|
||||
|
||||
public sealed partial class HashService(IHashids hasher) : IService
|
||||
{
|
||||
[GeneratedRegex(@"\A\b[0-9a-fA-F]+\b\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||
private static partial Regex HexHashRegex { get; }
|
||||
|
||||
[GeneratedRegex(@"\A[0-9a-fA-F]{32}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||
private static partial Regex Md5Regex { get; }
|
||||
|
||||
[GeneratedRegex(@"\A[0-9a-fA-F]{64}\Z", RegexOptions.None, matchTimeoutMilliseconds: 100)]
|
||||
private static partial Regex Sha256Regex { get; }
|
||||
|
||||
public static bool IsMd5Hash(string? value) =>
|
||||
!string.IsNullOrWhiteSpace(value) && Patterns.Md5Regex.IsMatch(value);
|
||||
!string.IsNullOrWhiteSpace(value) && Md5Regex.IsMatch(value);
|
||||
|
||||
public static bool IsSha256Hash(string? value) =>
|
||||
!string.IsNullOrWhiteSpace(value) && Patterns.Sha256Regex.IsMatch(value);
|
||||
!string.IsNullOrWhiteSpace(value) && Sha256Regex.IsMatch(value);
|
||||
|
||||
public static string? StringToSha256Hash(string? input) =>
|
||||
string.IsNullOrEmpty(input) ? null : Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input)));
|
||||
@@ -31,18 +39,17 @@ public sealed partial class HashService(IHashids hasher) : IService
|
||||
return Result.Ok(Convert.ToHexString(bytes).ToLowerInvariant());
|
||||
}
|
||||
|
||||
public Result<string> HashEncodeHex(string input) =>
|
||||
Result.FailIf(string.IsNullOrWhiteSpace(input) || !Patterns.HexHashRegex.IsMatch(input),
|
||||
ValidationError.Create("Input must be a valid hexadecimal string."))
|
||||
.ToResult(hasher.EncodeHex(input));
|
||||
public Result<string> HashEncodeHex(string input) => string.IsNullOrWhiteSpace(input) || !HexHashRegex.IsMatch(input)
|
||||
? Result.Fail<string>(ValidationError.Create("Input must be a valid hexadecimal string."))
|
||||
: Result.Ok(hasher.EncodeHex(input));
|
||||
|
||||
public Result<string> HashEncodeIntId(int id) =>
|
||||
Result.FailIf(id < 0, ValidationError.Create("Id cannot be negative."))
|
||||
.ToResult(hasher.Encode(id));
|
||||
public Result<string> HashEncodeIntId(int id) => id < 0
|
||||
? Result.Fail<string>(ValidationError.Create("Id cannot be negative."))
|
||||
: Result.Ok(hasher.Encode(id));
|
||||
|
||||
public Result<string> HashEncodeLongId(long id) =>
|
||||
Result.FailIf(id < 0, ValidationError.Create("Id cannot be negative."))
|
||||
.ToResult(hasher.EncodeLong(id));
|
||||
public Result<string> HashEncodeLongId(long id) => id < 0
|
||||
? Result.Fail<string>(ValidationError.Create("Id cannot be negative."))
|
||||
: Result.Ok(hasher.EncodeLong(id));
|
||||
|
||||
public Result<int> DecodeIntIdHash(string hash)
|
||||
{
|
||||
@@ -51,8 +58,9 @@ public sealed partial class HashService(IHashids hasher) : IService
|
||||
|
||||
int[] decoded = hasher.Decode(hash);
|
||||
|
||||
return Result.FailIf(decoded.Length <= 0, ValidationError.Create("Invalid or modified Int hash token."))
|
||||
.ToResult(decoded[0]);
|
||||
return decoded.Length == 1
|
||||
? Result.Ok(decoded[0]) :
|
||||
Result.Fail<int>(ValidationError.Create("Invalid or modified Int hash token."));
|
||||
}
|
||||
|
||||
public Result<long> DecodeLongIdHash(string hash)
|
||||
@@ -62,8 +70,8 @@ public sealed partial class HashService(IHashids hasher) : IService
|
||||
|
||||
long[] decoded = hasher.DecodeLong(hash);
|
||||
|
||||
return Result.FailIf(decoded.Length <= 0, ValidationError.Create("Invalid or modified Long hash token."))
|
||||
.ToResult(decoded[0]);
|
||||
return decoded.Length == 1 ? Result.Ok(decoded[0]) :
|
||||
Result.Fail<long>(ValidationError.Create("Invalid or modified Long hash token."));
|
||||
}
|
||||
|
||||
public async Task<Result<string>> DecodeHexHash(string hex) =>
|
||||
@@ -71,7 +79,8 @@ public sealed partial class HashService(IHashids hasher) : IService
|
||||
{
|
||||
string decoded = hasher.DecodeHex(hex);
|
||||
|
||||
return Result.FailIf(string.IsNullOrEmpty(decoded), ValidationError.Create("Invalid or corrupted hex hash."))
|
||||
.ToResult(decoded);
|
||||
return string.IsNullOrEmpty(decoded)
|
||||
? Result.Fail<string>(ValidationError.Create("Invalid or corrupted hex hash."))
|
||||
: Result.Ok(decoded);
|
||||
}));
|
||||
}
|
||||
@@ -1,10 +1,31 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Shared;
|
||||
|
||||
public sealed class IdentityCacheService(IdentityService service) : ServiceCacheBase, IIdentityService
|
||||
public sealed class IdentityCacheService(IdentityService service, IDistributedCache cache) : ServiceCacheBase, IIdentityService
|
||||
{
|
||||
public async ValueTask<Result> UserIsAuthorisedAsync(Guid userId, CancellationToken cancellationToken = default) =>
|
||||
await service.UserIsAuthorisedAsync(userId, cancellationToken);
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"userisauthorised-{userId}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.UserIsAuthorisedAsync(userId, cancellationToken); ;
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, "true", cacheOptions, cancellationToken);
|
||||
|
||||
return Result.Ok();
|
||||
}
|
||||
|
||||
return cached.Equals("true", StringComparison.OrdinalIgnoreCase)
|
||||
? Result.Ok()
|
||||
: Result.Fail(NotFoundError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ public sealed class IdentityService(IDbContextFactory<ApplicationDbContext> cont
|
||||
var userExistsAndActive = await context.Users
|
||||
.AnyAsync(u => u.Id == userId && u.Status == ActivityStatus.Active, cancellationToken);
|
||||
|
||||
return Result.OkIf(userExistsAndActive, NotFoundError.Create());
|
||||
return Result.FailIf(!userExistsAndActive, NotFoundError.Create());
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -29,16 +29,16 @@ public sealed class TokenService(ISecurityConnectApi connectApi, IOptions<Securi
|
||||
{
|
||||
var tokenResponse = JsonSerializer.Deserialize<Domain.Security.Models.TokenResponse>(contentRaw);
|
||||
|
||||
return Result.FailIf(string.IsNullOrWhiteSpace(tokenResponse?.AccessToken),
|
||||
NotFoundError.Create("Authentication succeeded, but no access token was found in the response payload."))
|
||||
.ToResult(tokenResponse!);
|
||||
return !string.IsNullOrWhiteSpace(tokenResponse?.AccessToken)
|
||||
? Result.Ok(tokenResponse)
|
||||
: Result.Fail<Domain.Security.Models.TokenResponse>(NotFoundError.Create("Authentication succeeded, but no access token was found in the response payload."));
|
||||
}
|
||||
|
||||
return await Result.Try((Func<ValueTask<Result<Domain.Security.Models.TokenResponse>>>)(async () =>
|
||||
{
|
||||
var errorResult = JsonSerializer.Deserialize<TokenErrorResponse>(contentRaw);
|
||||
|
||||
return Result.OkIf(errorResult is not null,
|
||||
return Result.FailIf(errorResult != null,
|
||||
CreateError.Create($"{errorResult!.Error}: {errorResult.ErrorDescription}"));
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Tools;
|
||||
|
||||
public sealed class ToolCacheService(ToolService service, IDistributedCache cache) : ServiceCacheBase, IToolService
|
||||
{
|
||||
public async ValueTask<Result<string>> EncodeLong(long id, CancellationToken cancellationToken) =>
|
||||
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"encodelonghash-{id}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = (await service.EncodeLong(id).ConfigureAwait(false));
|
||||
|
||||
if (result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value, cacheOptions, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached);
|
||||
}));
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Tools;
|
||||
|
||||
public sealed class ToolService(HashService service) : IToolService
|
||||
{
|
||||
public ValueTask<Result<string>> EncodeLong(long id, CancellationToken cancellationToken = default) =>
|
||||
ValueTask.FromResult(service.HashEncodeLongId(id));
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using PostFundManagement.Application.Abstractions;
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Extensions;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -22,19 +21,20 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach
|
||||
|
||||
if (userResult.IsFailed) return userResult;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, userResult.Value.ToJson(), cacheOptions, cancellationToken);
|
||||
await cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(userResult.Value), cacheOptions, cancellationToken);
|
||||
|
||||
return userResult;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<User>());
|
||||
var restored = JsonSerializer.Deserialize<User>(cached)!;
|
||||
|
||||
return Result.Ok(restored);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<User[]>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getusers-{pagination.Page}-{pagination.PageSize}-" +
|
||||
$"{request.Email}-{request.Status}-{request.SortBy}-{request.SortDescending}").Value;
|
||||
var cacheKey = HashService.ToMd5Hash($"getusers-{pagination.Page}-{pagination.PageSize}-{request.Email}-{request.Status}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
@@ -44,13 +44,14 @@ public sealed class UserCacheService(UserService service, IDistributedCache cach
|
||||
|
||||
if (usersResult.IsFailed) return usersResult;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, usersResult.Value.ToJson(),
|
||||
cacheOptions, cancellationToken);
|
||||
await cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(usersResult.Value), cacheOptions, cancellationToken);
|
||||
|
||||
return usersResult;
|
||||
}
|
||||
|
||||
return Result.Ok(cached.FromJson<User[]>());
|
||||
var restored = JsonSerializer.Deserialize<User[]>(cached)!;
|
||||
|
||||
return Result.Ok(restored);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> SyncUserAsync(Guid userId, string email, CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
|
||||
context.Users.Update(user);
|
||||
}
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<User[]>> GetUsersAsync(Pagination pagination, UserFilterRequest request, CancellationToken cancellationToken = default) =>
|
||||
@@ -74,7 +74,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
|
||||
.Where(f => f.Id == userId)
|
||||
.ExecuteUpdateAsync(f => f.SetProperty(u => u.Status, status), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.FailIf(changedTotal <= 0, UpdateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken) =>
|
||||
@@ -85,7 +85,8 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> contextF
|
||||
var user = await context.Users.AsNoTracking()
|
||||
.FirstOrDefaultAsync(f => f.Id == userId, cancellationToken);
|
||||
|
||||
return Result.FailIf(user is null, NotFoundError.New)
|
||||
.ToResult(user!.Map());
|
||||
return user is not null
|
||||
? Result.Ok(user.Map())
|
||||
: Result.Fail(NotFoundError.New);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# GEMINI_API_KEY: Required for Gemini AI API calls.
|
||||
# AI Studio automatically injects this at runtime from user secrets.
|
||||
# Users configure this via the Secrets panel in the AI Studio UI.
|
||||
GEMINI_API_KEY="MY_GEMINI_API_KEY"
|
||||
|
||||
# APP_URL: The URL where this applet is hosted.
|
||||
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
|
||||
# Used for self-referential links, OAuth callbacks, and API endpoints.
|
||||
APP_URL="MY_APP_URL"
|
||||
|
||||
# OIDC SecuritySettings
|
||||
VITE_OIDC_AUTHORITY="https://sts.security.khongisa.co.za"
|
||||
VITE_OIDC_AUDIENCE="pfm-api-dev"
|
||||
VITE_OIDC_CLIENT_ID="pfm-scalar-dev"
|
||||
VITE_OIDC_CLIENT_SECRET="secret_71f88570a46eb22eb5270b47f69e3332c84d8db85d008f0276cb7431c8872d27"
|
||||
|
||||
# PFM Backend API Base URL
|
||||
VITE_API_BASE_URL="https://api.ifri.khongisa.co.za/"
|
||||
@@ -1,8 +0,0 @@
|
||||
node_modules/
|
||||
build/
|
||||
dist/
|
||||
coverage/
|
||||
.DS_Store
|
||||
*.log
|
||||
.env*
|
||||
!.env.example
|
||||
@@ -1,41 +0,0 @@
|
||||
# Stage 1: Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Enable corepack to use Yarn
|
||||
RUN corepack enable
|
||||
|
||||
# Copy dependency definitions from the subdirectory
|
||||
COPY PostFundManagement.ConsoleUI/package*.json PostFundManagement.ConsoleUI/yarn.lock* ./
|
||||
|
||||
# Install dependencies via Yarn
|
||||
RUN yarn install --frozen-lockfile || yarn install
|
||||
|
||||
# Copy application source from the subdirectory
|
||||
COPY PostFundManagement.ConsoleUI/ ./
|
||||
|
||||
# Build Vite application for production
|
||||
RUN yarn build
|
||||
|
||||
# Stage 2: Production Nginx Server
|
||||
FROM nginx:alpine
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
# Copy nginx configuration from stage 1 build directory
|
||||
COPY --from=builder /app/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy compiled static assets from build stage
|
||||
COPY --from=builder /app/dist ./
|
||||
|
||||
# Copy entrypoint script for dynamic runtime environment variables
|
||||
COPY --from=builder /app/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Expose port 3000
|
||||
EXPOSE 3000
|
||||
|
||||
# Set entrypoint and default command
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user