From 8100518549ea92d3cec561d7f9a91271ac80a86e Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Sun, 4 Oct 2026 08:18:43 +0200 Subject: [PATCH] Hardened API and catered for Partner role query filter bypass --- .../GetApplicantInviteEndpoint.cs | 4 +- .../GetApplicantInvitesEndpoint.cs | 4 +- .../Evidence/CreateIndicatorEndpoint.cs | 4 +- .../Evidence/CreateMilestoneEndpoint.cs | 3 +- .../Endpoints/Evidence/GetEvidenceEndpoint.cs | 4 +- .../Evidence/GetEvidenceUrlEndpoint.cs | 8 +-- .../Evidence/GetIndicatorEndpoint.cs | 4 +- .../Evidence/UpdateEvidenceEndpoint.cs | 4 +- .../Execution/CreateDisbursementEndpoint.cs | 4 +- .../Endpoints/Execution/CreateRiskEndpoint.cs | 4 +- .../Execution/GetDisbursementEndpoint.cs | 4 +- .../Endpoints/Execution/GetRiskEndpoint.cs | 4 +- .../Endpoints/Execution/GetRuleEndpoint.cs | 4 +- .../Governance/GetInstrumentsEndpoint.cs | 2 +- .../Governance/GetOrganisationEndpoint.cs | 4 +- .../Governance/GetPortfolioEndpoint.cs | 4 +- .../GetPortfolioProgrammesEndpoint.cs | 4 +- .../Endpoints/Grants/CreateAwardEndpoint.cs | 4 +- .../Endpoints/Grants/CreateBudgetEndpoint.cs | 6 +- .../Grants/CreateContractEndpoint.cs | 3 +- .../Endpoints/Grants/GetAwardEndpoint.cs | 4 +- .../Endpoints/Grants/GetBudgetEndpoint.cs | 4 +- .../Endpoints/Grants/GetContractEndpoint.cs | 4 +- .../Grants/GetContractUrlEndpoint.cs | 4 +- .../Grants/UpdateContractEndpoint.cs | 4 +- .../Grants/UploadContractEndpoint.cs | 1 - .../Projects/CreateActivityEndpoint.cs | 3 +- .../Projects/CreateProgrammeEndpoint.cs | 4 +- .../Projects/CreateProjectEndpoint.cs | 4 +- .../Endpoints/Projects/GetActivityEndpoint.cs | 4 +- .../Projects/GetBeneficiariesEndpoint.cs | 4 +- .../Projects/GetBeneficiaryEndpoint.cs | 4 +- .../Projects/GetChangeRequestEndpoint.cs | 4 +- .../Projects/GetChangeRequestsEndpoint.cs | 4 +- .../Projects/GetComplianceItemEndpoint.cs | 4 +- .../Projects/GetComplianceItemsEndpoint.cs | 4 +- .../Endpoints/Projects/GetIssueEndpoint.cs | 4 +- .../Endpoints/Projects/GetIssuesEndpoint.cs | 4 +- .../Projects/GetMilestoneEndpoint.cs | 4 +- .../Endpoints/Projects/GetOutcomeEndpoint.cs | 4 +- .../Endpoints/Projects/GetOutcomesEndpoint.cs | 4 +- .../Projects/GetProgrammeEndpoint.cs | 3 +- .../Projects/GetProgrammeProjectsEndpoint.cs | 4 +- .../Endpoints/Projects/GetProjectEndpoint.cs | 4 +- .../Projects/GetSiteVisitEndpoint.cs | 4 +- .../Projects/GetSiteVisitsEndpoint.cs | 4 +- .../Projects/UpdateProgrammeEndpoint.cs | 3 +- .../Projects/UpdateProjectStatusEndpoint.cs | 3 +- PostFundManagement.Api/Program.cs | 10 +++ .../Filters/ActiveUserFilter.cs | 9 +-- .../Filters/UserRoleFilter.cs | 25 ++++++++ .../Communications/CommunicationsService.cs | 10 +-- .../Services/Evidence/EvidenceService.cs | 24 +++---- .../Services/Execution/ExecutionService.cs | 22 +++---- .../Services/Governance/GovernanceService.cs | 28 ++++---- .../Services/Grants/GrantsService.cs | 28 ++++---- .../Services/Projects/ProjectService.cs | 64 +++++++++---------- .../Services/Users/UserService.cs | 4 +- .../Abstractions/ITenantContextService.cs | 1 + .../Database/ApplicationDbContext.cs | 10 +-- .../DesignTimeTenantContextService.cs | 1 + .../Database/Services/TenantContextService.cs | 2 + 62 files changed, 232 insertions(+), 193 deletions(-) create mode 100644 PostFundManagement.Application/Filters/UserRoleFilter.cs diff --git a/PostFundManagement.Api/Endpoints/Communications/GetApplicantInviteEndpoint.cs b/PostFundManagement.Api/Endpoints/Communications/GetApplicantInviteEndpoint.cs index 3493d2b..549ab46 100644 --- a/PostFundManagement.Api/Endpoints/Communications/GetApplicantInviteEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Communications/GetApplicantInviteEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetApplicantInviteEndpoint : IEndpoint await service.GetApplicantInviteAsync(applicantUserId, cancellationToken) switch { { IsSuccess: true, Value: var invite } => Results.Ok(invite), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()), }) .WithDescription("Gets an applicant's invite") @@ -27,8 +27,8 @@ public sealed class GetApplicantInviteEndpoint : IEndpoint .WithName(typeof(GetApplicantInviteEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Communications) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Communications/GetApplicantInvitesEndpoint.cs b/PostFundManagement.Api/Endpoints/Communications/GetApplicantInvitesEndpoint.cs index 94782fa..2bc28b8 100644 --- a/PostFundManagement.Api/Endpoints/Communications/GetApplicantInvitesEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Communications/GetApplicantInvitesEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetApplicantInvitesEndpoint : IEndpoint await service.GetApplicantInvitesAsync(applicantUserId, cancellationToken) switch { { IsSuccess: true, Value: var invite } => Results.Ok(invite), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()), }) .WithDescription("Gets an applicant's invite list") @@ -27,8 +27,8 @@ public sealed class GetApplicantInvitesEndpoint : IEndpoint .WithName(typeof(GetApplicantInvitesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Communications) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Evidence/CreateIndicatorEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/CreateIndicatorEndpoint.cs index febc0ba..5642f0d 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/CreateIndicatorEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/CreateIndicatorEndpoint.cs @@ -19,7 +19,7 @@ public class CreateIndicatorEndpoint : IEndpoint await service.CreateIndicatorAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -28,8 +28,8 @@ public class CreateIndicatorEndpoint : IEndpoint .WithName(typeof(CreateIndicatorEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence); diff --git a/PostFundManagement.Api/Endpoints/Evidence/CreateMilestoneEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/CreateMilestoneEndpoint.cs index 7f40348..5472827 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/CreateMilestoneEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/CreateMilestoneEndpoint.cs @@ -19,7 +19,7 @@ public class CreateMilestoneEndpoint : IEndpoint await service.CreateMilestoneAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -30,6 +30,7 @@ public class CreateMilestoneEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence); } diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceEndpoint.cs index 8cc45f0..1e50d5f 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetEvidenceEndpoint : IEndpoint { { IsSuccess: true, Value: var evidence } => Results.Ok(evidence), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific evidence record and assiciated file URLs") @@ -27,9 +27,9 @@ public sealed class GetEvidenceEndpoint : IEndpoint .WithName(typeof(GetEvidenceEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status403Forbidden) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceUrlEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceUrlEndpoint.cs index ba0d548..77ed22c 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceUrlEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/GetEvidenceUrlEndpoint.cs @@ -19,17 +19,17 @@ public sealed class GetEvidenceUrlEndpoint : IEndpoint { { IsSuccess: true, Value: var evidenceUrl } => Results.Ok(evidenceUrl), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a secure download URL of the evidence file, URL expires (default: 15min)") .RequireAuthorization(Constants.MainScope) .WithName(typeof(GetEvidenceUrlEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) - .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) - .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status403Forbidden) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Evidence/GetIndicatorEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/GetIndicatorEndpoint.cs index 7f38255..619a167 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/GetIndicatorEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/GetIndicatorEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetIndicatorEndpoint : IEndpoint { { IsSuccess: true, Value: var indicator } => Results.Ok(indicator), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific indicator.") @@ -28,9 +28,9 @@ public sealed class GetIndicatorEndpoint : IEndpoint .WithName(typeof(GetIndicatorEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status403Forbidden) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Evidence/UpdateEvidenceEndpoint.cs b/PostFundManagement.Api/Endpoints/Evidence/UpdateEvidenceEndpoint.cs index 8f2ade3..3a5d310 100644 --- a/PostFundManagement.Api/Endpoints/Evidence/UpdateEvidenceEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Evidence/UpdateEvidenceEndpoint.cs @@ -19,7 +19,7 @@ public sealed class UpdateEvidenceEndpoint : IEndpoint await service.UpdateEvidenceAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -29,9 +29,9 @@ public sealed class UpdateEvidenceEndpoint : IEndpoint .WithName(typeof(UpdateEvidenceEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Evidence); diff --git a/PostFundManagement.Api/Endpoints/Execution/CreateDisbursementEndpoint.cs b/PostFundManagement.Api/Endpoints/Execution/CreateDisbursementEndpoint.cs index 4d31ad0..7905d82 100644 --- a/PostFundManagement.Api/Endpoints/Execution/CreateDisbursementEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Execution/CreateDisbursementEndpoint.cs @@ -19,7 +19,7 @@ public class CreateDisbursementEndpoint : IEndpoint await service.CreateDisbursementAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()), }) @@ -28,9 +28,9 @@ public class CreateDisbursementEndpoint : IEndpoint .WithName(typeof(CreateDisbursementEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution); } diff --git a/PostFundManagement.Api/Endpoints/Execution/CreateRiskEndpoint.cs b/PostFundManagement.Api/Endpoints/Execution/CreateRiskEndpoint.cs index f6bcfc2..48e3bce 100644 --- a/PostFundManagement.Api/Endpoints/Execution/CreateRiskEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Execution/CreateRiskEndpoint.cs @@ -19,7 +19,7 @@ public class CreateRiskEndpoint : IEndpoint await service.CreateRiskAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -28,9 +28,9 @@ public class CreateRiskEndpoint : IEndpoint .WithName(typeof(CreateRiskEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution); } diff --git a/PostFundManagement.Api/Endpoints/Execution/GetDisbursementEndpoint.cs b/PostFundManagement.Api/Endpoints/Execution/GetDisbursementEndpoint.cs index 5f486ce..827ab0f 100644 --- a/PostFundManagement.Api/Endpoints/Execution/GetDisbursementEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Execution/GetDisbursementEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetDisbursementEndpoint : IEndpoint { { IsSuccess: true, Value: var disbursement } => Results.Ok(disbursement), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific disbursement") @@ -28,9 +28,9 @@ public sealed class GetDisbursementEndpoint : IEndpoint .WithName(typeof(GetDisbursementEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Execution/GetRiskEndpoint.cs b/PostFundManagement.Api/Endpoints/Execution/GetRiskEndpoint.cs index 8939886..5478fde 100644 --- a/PostFundManagement.Api/Endpoints/Execution/GetRiskEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Execution/GetRiskEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetRiskEndpoint : IEndpoint { { IsSuccess: true, Value: var risk } => Results.Ok(risk), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific risk") @@ -28,8 +28,8 @@ public sealed class GetRiskEndpoint : IEndpoint .WithName(typeof(GetRiskEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Execution/GetRuleEndpoint.cs b/PostFundManagement.Api/Endpoints/Execution/GetRuleEndpoint.cs index 03d1a18..f0bca27 100644 --- a/PostFundManagement.Api/Endpoints/Execution/GetRuleEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Execution/GetRuleEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetRuleEndpoint : IEndpoint { { IsSuccess: true, Value: var rules } => Results.Ok(rules), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific programme rule") @@ -28,8 +28,8 @@ public sealed class GetRuleEndpoint : IEndpoint .WithName(typeof(GetRuleEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Execution) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Governance/GetInstrumentsEndpoint.cs b/PostFundManagement.Api/Endpoints/Governance/GetInstrumentsEndpoint.cs index fba8f5d..937bf4a 100644 --- a/PostFundManagement.Api/Endpoints/Governance/GetInstrumentsEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Governance/GetInstrumentsEndpoint.cs @@ -18,7 +18,7 @@ public class GetInstrumentsEndpoint : IEndpoint CancellationToken cancellationToken = default) => await service.GetInstrumentsAsync(programmeId, cancellationToken) switch { - { IsSuccess: true, Value: var instrumenrs } => Results.Ok(instrumenrs), + { IsSuccess: true, Value: var instruments } => Results.Ok(instruments), var r when r.HasError() => Results.Forbid(), var r => Results.InternalServerError(r.Errors.ToProblems()), }) diff --git a/PostFundManagement.Api/Endpoints/Governance/GetOrganisationEndpoint.cs b/PostFundManagement.Api/Endpoints/Governance/GetOrganisationEndpoint.cs index aeebd0f..3c12110 100644 --- a/PostFundManagement.Api/Endpoints/Governance/GetOrganisationEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Governance/GetOrganisationEndpoint.cs @@ -20,7 +20,7 @@ public class GetOrganisationEndpoint : IEndpoint { { IsSuccess: true, Value: var organisation } => Results.Ok(organisation), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get organization profile by ID") @@ -28,9 +28,9 @@ public class GetOrganisationEndpoint : IEndpoint .WithName(typeof(GetOrganisationEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status403Forbidden) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Governance/GetPortfolioEndpoint.cs b/PostFundManagement.Api/Endpoints/Governance/GetPortfolioEndpoint.cs index 017cf17..9119acc 100644 --- a/PostFundManagement.Api/Endpoints/Governance/GetPortfolioEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Governance/GetPortfolioEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetPortfolioEndpoint : IEndpoint await service.GetPortfolioAsync(portfolioIdHash: portfolioId, cancellationToken) switch { { IsSuccess: true, Value: var portfolio } => Results.Ok(portfolio), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get a specific portfolios") @@ -27,9 +27,9 @@ public sealed class GetPortfolioEndpoint : IEndpoint .WithName(typeof(GetPortfolioEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Governance/GetPortfolioProgrammesEndpoint.cs b/PostFundManagement.Api/Endpoints/Governance/GetPortfolioProgrammesEndpoint.cs index 1f56ca6..b6825e8 100644 --- a/PostFundManagement.Api/Endpoints/Governance/GetPortfolioProgrammesEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Governance/GetPortfolioProgrammesEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetPortfolioProgrammesEndpoint : IEndpoint await service.GetPortfolioProgrammesAsync(portfolioId, cancellationToken) switch { { IsSuccess: true, Value: var portfolios } => Results.Ok(portfolios), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.Forbid(), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -28,9 +28,9 @@ public sealed class GetPortfolioProgrammesEndpoint : IEndpoint .WithName(typeof(GetPortfolioProgrammesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Governance) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Grants/CreateAwardEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/CreateAwardEndpoint.cs index a1383b3..edf4f19 100644 --- a/PostFundManagement.Api/Endpoints/Grants/CreateAwardEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/CreateAwardEndpoint.cs @@ -19,7 +19,7 @@ public class CreateAwardEndpoint : IEndpoint await service.CreateAwardAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) @@ -30,7 +30,7 @@ public class CreateAwardEndpoint : IEndpoint .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants); diff --git a/PostFundManagement.Api/Endpoints/Grants/CreateBudgetEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/CreateBudgetEndpoint.cs index 70efa13..471615e 100644 --- a/PostFundManagement.Api/Endpoints/Grants/CreateBudgetEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/CreateBudgetEndpoint.cs @@ -20,8 +20,8 @@ public class CreateBudgetEndpoint : IEndpoint { { IsSuccess: true } => Results.Ok(), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), - var r when r.HasError() => Results.NotFound(), - var r when r.HasError() => Results.BadRequest(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), + var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Create a new budget line (M05)") @@ -29,8 +29,8 @@ public class CreateBudgetEndpoint : IEndpoint .WithName(typeof(CreateBudgetEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized) diff --git a/PostFundManagement.Api/Endpoints/Grants/CreateContractEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/CreateContractEndpoint.cs index 0c77962..cc59bbb 100644 --- a/PostFundManagement.Api/Endpoints/Grants/CreateContractEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/CreateContractEndpoint.cs @@ -19,7 +19,7 @@ public class CreateContractEndpoint : IEndpoint await service.CreateContractAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -29,6 +29,7 @@ public class CreateContractEndpoint : IEndpoint .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Grants); diff --git a/PostFundManagement.Api/Endpoints/Grants/GetAwardEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/GetAwardEndpoint.cs index 530053c..3ae4908 100644 --- a/PostFundManagement.Api/Endpoints/Grants/GetAwardEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/GetAwardEndpoint.cs @@ -20,7 +20,7 @@ public class GetAwardEndpoint : IEndpoint { { IsSuccess: true, Value: var award } => Results.Ok(award), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get award by ID") @@ -30,7 +30,7 @@ public class GetAwardEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Grants/GetBudgetEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/GetBudgetEndpoint.cs index cfbcd28..70389c7 100644 --- a/PostFundManagement.Api/Endpoints/Grants/GetBudgetEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/GetBudgetEndpoint.cs @@ -20,7 +20,7 @@ public class GetBudgetEndpoint : IEndpoint { { IsSuccess: true, Value: var budget } => Results.Ok(budget), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get budget by ID") @@ -28,7 +28,7 @@ public class GetBudgetEndpoint : IEndpoint .WithName(typeof(GetBudgetEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status500InternalServerError) diff --git a/PostFundManagement.Api/Endpoints/Grants/GetContractEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/GetContractEndpoint.cs index af71f64..669fd02 100644 --- a/PostFundManagement.Api/Endpoints/Grants/GetContractEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/GetContractEndpoint.cs @@ -20,7 +20,7 @@ public class GetContractEndpoint : IEndpoint { { IsSuccess: true, Value: var contract } => Results.Ok(contract), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get contract by ID") @@ -28,7 +28,7 @@ public class GetContractEndpoint : IEndpoint .WithName(typeof(GetContractEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) .Produces(StatusCodes.Status500InternalServerError) diff --git a/PostFundManagement.Api/Endpoints/Grants/GetContractUrlEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/GetContractUrlEndpoint.cs index b814647..093fd57 100644 --- a/PostFundManagement.Api/Endpoints/Grants/GetContractUrlEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/GetContractUrlEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetContractUrlEndpoint : IEndpoint { { IsSuccess: true, Value: var contractUrl} => Results.Ok(contractUrl), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a secure download URL, has expiry period (default 15min)") @@ -29,7 +29,7 @@ public sealed class GetContractUrlEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Grants/UpdateContractEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/UpdateContractEndpoint.cs index 854a3ab..c3a8a76 100644 --- a/PostFundManagement.Api/Endpoints/Grants/UpdateContractEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/UpdateContractEndpoint.cs @@ -19,7 +19,7 @@ public sealed class UpdateContractEndpoint : IEndpoint await service.UpdateContractAsync(request, cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) @@ -30,7 +30,7 @@ public sealed class UpdateContractEndpoint : IEndpoint .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status404NotFound) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants) diff --git a/PostFundManagement.Api/Endpoints/Grants/UploadContractEndpoint.cs b/PostFundManagement.Api/Endpoints/Grants/UploadContractEndpoint.cs index 854927b..7438d24 100644 --- a/PostFundManagement.Api/Endpoints/Grants/UploadContractEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Grants/UploadContractEndpoint.cs @@ -40,7 +40,6 @@ public sealed class UploadContractEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Grants) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/CreateActivityEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/CreateActivityEndpoint.cs index 7dd86b4..9398d6a 100644 --- a/PostFundManagement.Api/Endpoints/Projects/CreateActivityEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/CreateActivityEndpoint.cs @@ -19,7 +19,7 @@ public class CreateActivityEndpoint : IEndpoint await service.CreateActivityAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } result => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -28,6 +28,7 @@ public class CreateActivityEndpoint : IEndpoint .WithName(typeof(CreateActivityEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .Produces(StatusCodes.Status401Unauthorized) diff --git a/PostFundManagement.Api/Endpoints/Projects/CreateProgrammeEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/CreateProgrammeEndpoint.cs index af829ec..f9ab7db 100644 --- a/PostFundManagement.Api/Endpoints/Projects/CreateProgrammeEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/CreateProgrammeEndpoint.cs @@ -19,7 +19,7 @@ public sealed class CreateProgrammeEndpoint : IEndpoint await service.CreateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(), var r => Results.InternalServerError(r.Errors.ToProblems()) @@ -29,9 +29,9 @@ public sealed class CreateProgrammeEndpoint : IEndpoint .WithName(typeof(CreateProgrammeEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects); } diff --git a/PostFundManagement.Api/Endpoints/Projects/CreateProjectEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/CreateProjectEndpoint.cs index 2fd5110..9864a47 100644 --- a/PostFundManagement.Api/Endpoints/Projects/CreateProjectEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/CreateProjectEndpoint.cs @@ -19,7 +19,7 @@ public class CreateProjectEndpoint : IEndpoint await service.CreateProjectAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } result => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) @@ -29,9 +29,9 @@ public class CreateProjectEndpoint : IEndpoint .WithName(typeof(CreateProjectEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status401Unauthorized) .WithTags(EndpointTags.Projects); } diff --git a/PostFundManagement.Api/Endpoints/Projects/GetActivityEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetActivityEndpoint.cs index 653bde4..507fafb 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetActivityEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetActivityEndpoint.cs @@ -19,7 +19,7 @@ public class GetActivityEndpoint : IEndpoint { { IsSuccess: true, Value: var activity } => Results.Ok(activity), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific activity by it's id") @@ -27,9 +27,9 @@ public class GetActivityEndpoint : IEndpoint .WithName(typeof(GetActivityEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetBeneficiariesEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetBeneficiariesEndpoint.cs index 35c30f9..5a47e75 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetBeneficiariesEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetBeneficiariesEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetBeneficiariesEndpoint : IEndpoint { { IsSuccess: true, Value: var beneficiaries } => Results.Ok(beneficiaries), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project beneficiaries") @@ -28,9 +28,9 @@ public sealed class GetBeneficiariesEndpoint : IEndpoint .WithName(typeof(GetBeneficiariesEndpoint).ToEndpointName("Retrieval")) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetBeneficiaryEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetBeneficiaryEndpoint.cs index 0af8e92..64cfb49 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetBeneficiaryEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetBeneficiaryEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetBeneficiaryEndpoint : IEndpoint { { IsSuccess: true, Value: var beneficiary } => Results.Ok(beneficiary), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project beneficiaries") @@ -28,9 +28,9 @@ public sealed class GetBeneficiaryEndpoint : IEndpoint .WithName(typeof(GetBeneficiariesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestEndpoint.cs index a71792d..813ca12 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetChangeRequestEndpoint : IEndpoint { { IsSuccess: true, Value: var changeRequest } => Results.Ok(changeRequest), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific project change request") @@ -28,9 +28,9 @@ public sealed class GetChangeRequestEndpoint : IEndpoint .WithName(typeof(GetChangeRequestEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestsEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestsEndpoint.cs index ecad747..2520126 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestsEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetChangeRequestsEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetChangeRequestsEndpoint : IEndpoint { { IsSuccess: true, Value: var changeRequests } => Results.Ok(changeRequests), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project change requests") @@ -28,9 +28,9 @@ public sealed class GetChangeRequestsEndpoint : IEndpoint .WithName(typeof(GetChangeRequestsEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemEndpoint.cs index 0a0ed2e..8668edb 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetComplianceItemEndpoint : IEndpoint { { IsSuccess: true, Value: var complianceItem } => Results.Ok(complianceItem), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific project compliance item") @@ -28,9 +28,9 @@ public sealed class GetComplianceItemEndpoint : IEndpoint .WithName(typeof(GetComplianceItemEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemsEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemsEndpoint.cs index 0322d94..b69ccfb 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemsEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetComplianceItemsEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetComplianceItemsEndpoint : IEndpoint { { IsSuccess: true, Value: var complianceItems } => Results.Ok(complianceItems), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project compliance items") @@ -28,9 +28,9 @@ public sealed class GetComplianceItemsEndpoint : IEndpoint .WithName(typeof(GetComplianceItemsEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetIssueEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetIssueEndpoint.cs index 983b642..0207627 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetIssueEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetIssueEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetIssueEndpoint : IEndpoint { { IsSuccess: true, Value: var issue } => Results.Ok(issue), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific project issue") @@ -28,9 +28,9 @@ public sealed class GetIssueEndpoint : IEndpoint .WithName(typeof(GetIssueEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetIssuesEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetIssuesEndpoint.cs index d60d5ad..d88f4ff 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetIssuesEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetIssuesEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetIssuesEndpoint : IEndpoint { { IsSuccess: true, Value: var issues } => Results.Ok(issues), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project issues") @@ -28,9 +28,9 @@ public sealed class GetIssuesEndpoint : IEndpoint .WithName(typeof(GetIssuesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetMilestoneEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetMilestoneEndpoint.cs index 245022e..81c7172 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetMilestoneEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetMilestoneEndpoint.cs @@ -20,7 +20,7 @@ public class GetMilestoneEndpoint : IEndpoint { { IsSuccess: true, Value: var milestone } => Results.Ok(milestone), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific milestone") @@ -28,9 +28,9 @@ public class GetMilestoneEndpoint : IEndpoint .WithName(typeof(GetMilestoneEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetOutcomeEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetOutcomeEndpoint.cs index ac81816..644898e 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetOutcomeEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetOutcomeEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetOutcomeEndpoint : IEndpoint { { IsSuccess: true, Value: var outcome } => Results.Ok(outcome), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets a specific project outcome") @@ -28,9 +28,9 @@ public sealed class GetOutcomeEndpoint : IEndpoint .WithName(typeof(GetOutcomeEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetOutcomesEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetOutcomesEndpoint.cs index e2f4e9f..4aa007f 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetOutcomesEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetOutcomesEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetOutcomesEndpoint : IEndpoint { { IsSuccess: true, Value: var outcomes } => Results.Ok(outcomes), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project beneficiary outcomes") @@ -28,9 +28,9 @@ public sealed class GetOutcomesEndpoint : IEndpoint .WithName(typeof(GetOutcomesEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetProgrammeEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetProgrammeEndpoint.cs index 8cb798d..6d4448e 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetProgrammeEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetProgrammeEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetProgrammeEndpoint : IEndpoint await service.GetProgrammeAsync(programmeId, cancellationToken) switch { { IsSuccess: true, Value: var programme } => Results.Ok(programme), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.Forbid(), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -30,6 +30,7 @@ public sealed class GetProgrammeEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetProgrammeProjectsEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetProgrammeProjectsEndpoint.cs index b698c23..d15d26e 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetProgrammeProjectsEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetProgrammeProjectsEndpoint.cs @@ -19,7 +19,7 @@ public sealed class GetProgrammeProjectsEndpoint : IEndpoint await service.GetProgrammeProjectsAsync(programmeId, cancellationToken) switch { { IsSuccess: true, Value: var projects } => Results.Ok(projects), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.Forbid(), var r => Results.InternalServerError(r.Errors.ToProblems()), }) @@ -28,9 +28,9 @@ public sealed class GetProgrammeProjectsEndpoint : IEndpoint .WithName(typeof(GetProgrammeProjectsEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetProjectEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetProjectEndpoint.cs index 0dc641f..209c1f6 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetProjectEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetProjectEndpoint.cs @@ -20,7 +20,7 @@ public class GetProjectEndpoint : IEndpoint { { IsSuccess: true, Value: var project } => Results.Ok(project), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Get project by ID") @@ -28,9 +28,9 @@ public class GetProjectEndpoint : IEndpoint .WithName(typeof(GetProjectEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitEndpoint.cs index bbe08f7..a0c17f2 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetSiteVisitEndpoint : IEndpoint { { IsSuccess: true, Value: var siteVisit } => Results.Ok(siteVisit), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project site visit") @@ -28,9 +28,9 @@ public sealed class GetSiteVisitEndpoint : IEndpoint .WithName(typeof(GetSiteVisitEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitsEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitsEndpoint.cs index 6eee950..4347e80 100644 --- a/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitsEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/GetSiteVisitsEndpoint.cs @@ -20,7 +20,7 @@ public sealed class GetSiteVisitsEndpoint : IEndpoint { { IsSuccess: true, Value: var siteVisits } => Results.Ok(siteVisits), var r when r.HasError() => Results.Forbid(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) .WithDescription("Gets project site visits") @@ -28,9 +28,9 @@ public sealed class GetSiteVisitsEndpoint : IEndpoint .WithName(typeof(GetSiteVisitsEndpoint).ToEndpointName()) .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) - .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects) .CacheOutput(); diff --git a/PostFundManagement.Api/Endpoints/Projects/UpdateProgrammeEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/UpdateProgrammeEndpoint.cs index 7ae0cc9..6ae5cda 100644 --- a/PostFundManagement.Api/Endpoints/Projects/UpdateProgrammeEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/UpdateProgrammeEndpoint.cs @@ -19,7 +19,7 @@ public sealed class UpdateProgrammeEndpoint : IEndpoint await service.UpdateProgrammeAsync(request, userId: context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -30,6 +30,7 @@ public sealed class UpdateProgrammeEndpoint : IEndpoint .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status403Forbidden) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects); diff --git a/PostFundManagement.Api/Endpoints/Projects/UpdateProjectStatusEndpoint.cs b/PostFundManagement.Api/Endpoints/Projects/UpdateProjectStatusEndpoint.cs index fd8fe41..773ae29 100644 --- a/PostFundManagement.Api/Endpoints/Projects/UpdateProjectStatusEndpoint.cs +++ b/PostFundManagement.Api/Endpoints/Projects/UpdateProjectStatusEndpoint.cs @@ -19,7 +19,7 @@ public class UpdateProjectStatusEndpoint : IEndpoint await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch { { IsSuccess: true } => Results.Ok(), - var r when r.HasError() => Results.NotFound(), + var r when r.HasError() => Results.NotFound(r.Errors.ToProblems()), var r when r.HasError() => Results.BadRequest(r.Errors.ToProblems()), var r => Results.InternalServerError(r.Errors.ToProblems()) }) @@ -29,6 +29,7 @@ public class UpdateProjectStatusEndpoint : IEndpoint .MapToApiVersion(new ApiVersion(1)) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status401Unauthorized) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError) .WithTags(EndpointTags.Projects); diff --git a/PostFundManagement.Api/Program.cs b/PostFundManagement.Api/Program.cs index ee03d59..c031e4e 100644 --- a/PostFundManagement.Api/Program.cs +++ b/PostFundManagement.Api/Program.cs @@ -29,6 +29,7 @@ builder.Services.AddApiVersioning(options => }); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddTransient(); builder.Services.AddEndpoints(Assembly.GetExecutingAssembly()); @@ -156,6 +157,15 @@ var versionGroups = new Dictionary var filter = context.HttpContext.RequestServices.GetRequiredService(); + return await filter.InvokeAsync(context, next); + }) + .AddEndpointFilter(async (context, next) => + { + if (HttpMethods.IsOptions(context.HttpContext.Request.Method)) + return await next(context); + + var filter = context.HttpContext.RequestServices.GetRequiredService(); + return await filter.InvokeAsync(context, next); }) } diff --git a/PostFundManagement.Application/Filters/ActiveUserFilter.cs b/PostFundManagement.Application/Filters/ActiveUserFilter.cs index f7ecb2c..dba5679 100644 --- a/PostFundManagement.Application/Filters/ActiveUserFilter.cs +++ b/PostFundManagement.Application/Filters/ActiveUserFilter.cs @@ -15,17 +15,13 @@ public sealed class ActiveUserFilter(IIdentityService identityService) : IEndpoi var (decoded, userId) = principal.GetUserId(); var (emailDecoded, email) = principal.GetUserEmail(); - var (roleDecoded, role) = principal.GetUserRole(); - + if (!decoded) return Results.Json(new { message = "Invalid user identifier in token." }, statusCode: StatusCodes.Status401Unauthorized); if (!emailDecoded) return Results.Json(new { message = "Invalid/missing user email in token." }, statusCode: StatusCodes.Status401Unauthorized); - if (!roleDecoded) - return Results.Json(new { message = "Invalid/missing user role in token." }, statusCode: StatusCodes.Status401Unauthorized); - var path = context.HttpContext.Request.Path.Value; if (!string.IsNullOrWhiteSpace(path) && !path!.EndsWith("integration/sync", StringComparison.InvariantCultureIgnoreCase)) @@ -38,8 +34,7 @@ public sealed class ActiveUserFilter(IIdentityService identityService) : IEndpoi context.HttpContext.Items[UserIdItemKey] = userId; context.HttpContext.Items[EmailtemKey] = email; - context.HttpContext.Items[UserRoleItemKey] = role; - + return await next(context); } } diff --git a/PostFundManagement.Application/Filters/UserRoleFilter.cs b/PostFundManagement.Application/Filters/UserRoleFilter.cs new file mode 100644 index 0000000..ca54e14 --- /dev/null +++ b/PostFundManagement.Application/Filters/UserRoleFilter.cs @@ -0,0 +1,25 @@ +using PostFundManagement.Application.Extensions; +using PostFundManagement.Domain.Abstractions; +using static PostFundManagement.Domain.Extensions.Constants; + +namespace PostFundManagement.Application.Filters; + +public sealed class UserRoleFilter(ITenantContextService tenantContext) : IEndpointFilter +{ + public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) + { + var principal = context.HttpContext.User; + + var (roleDecoded, role) = principal.GetUserRole(); + + if (!roleDecoded) + return Results.Json(new { message = "Invalid/missing user role in token." }, statusCode: StatusCodes.Status401Unauthorized); + + context.HttpContext.Items[UserRoleItemKey] = role; + + if (role.Equals("Partner", StringComparison.InvariantCultureIgnoreCase)) + tenantContext.ByPassTenantQueryFilter = true; + + return await next(context); + } +} diff --git a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs index 6b4bb9e..7151876 100644 --- a/PostFundManagement.Application/Services/Communications/CommunicationsService.cs +++ b/PostFundManagement.Application/Services/Communications/CommunicationsService.cs @@ -46,7 +46,7 @@ public sealed class CommunicationsService(IDbContextFactory> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -72,8 +72,8 @@ public sealed class CommunicationsService(IDbContextFactory o.Id) .FirstOrDefaultAsync(i => i.Recipient == applicantUserId, cancellationToken); - return Result.FailIf(invite is null, NotFoundError.Create("No active invite found")) - .ToResult(invite!.Map()); + return Result.FailIf(invite == null, NotFoundError.Create("No active invite found")) + .ToResult(invite?.Map()!); })); public async Task> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) => @@ -86,7 +86,7 @@ public sealed class CommunicationsService(IDbContextFactory o.Id) .ToListAsync(cancellationToken); - return Result.Ok(invites.Select(i => i.Map()).ToArray()); + return Result.Ok(invites?.Select(i => i.Map()).ToArray() ?? []); })); public async Task CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -162,6 +162,6 @@ public sealed class CommunicationsService(IDbContextFactory n.Id == decodeResult.Value, cancellationToken); return Result.FailIf(notification is null, NotFoundError.Create("Notification not found")) - .ToResult(notification!.Map()); + .ToResult(notification?.Map()!); })); } diff --git a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs index e293722..664f0ce 100644 --- a/PostFundManagement.Application/Services/Evidence/EvidenceService.cs +++ b/PostFundManagement.Application/Services/Evidence/EvidenceService.cs @@ -46,7 +46,7 @@ public sealed class EvidenceService(IDbContextFactory cont context.Evidences.Add(evidence); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create evidence.")); })); public async Task RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) => @@ -65,7 +65,7 @@ public sealed class EvidenceService(IDbContextFactory cont .SetProperty(f => f.ActualAmount, amount) .SetProperty(f => f.CreatedBy, userId), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update indicator.")); })); public async Task> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -79,7 +79,7 @@ public sealed class EvidenceService(IDbContextFactory cont .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(indicators.Select(i => i.Map()).ToArray()); + return Result.Ok(indicators?.Select(i => i.Map()).ToArray() ?? []); })); public async Task> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -93,7 +93,7 @@ public sealed class EvidenceService(IDbContextFactory cont .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(evidence.Select(i => i.Map()).ToArray()); + return Result.Ok(evidence?.Select(i => i.Map()).ToArray() ?? []); })); public async Task CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -142,7 +142,7 @@ public sealed class EvidenceService(IDbContextFactory cont CreatedAt = DateTime.UtcNow }); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create indicator.")); })); public async Task> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) => @@ -158,8 +158,8 @@ public sealed class EvidenceService(IDbContextFactory 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 Result.FailIf(indicator is null, NotFoundError.Create("Indicator not found.")) + .ToResult(indicator?.Map()!); })); public async Task> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) => @@ -175,8 +175,8 @@ public sealed class EvidenceService(IDbContextFactory 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 Result.FailIf(evidence is null, NotFoundError.Create("Evidence not found.")) + .ToResult(evidence?.Map()!); })); public async Task> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => @@ -196,7 +196,7 @@ public sealed class EvidenceService(IDbContextFactory cont .ThenByDescending(o => o.UpdatedAt) .ToListAsync(cancellationToken); - return Result.Ok(evidences.Select(e => e.Map()).ToArray()); + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); })); public async Task> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) => @@ -216,7 +216,7 @@ public sealed class EvidenceService(IDbContextFactory cont .ThenBy(o => o.UpdatedAt) .ToListAsync(cancellationToken); - return Result.Ok(evidences.Select(e => e.Map()).ToArray()); + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); })); public async Task UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -235,7 +235,7 @@ public sealed class EvidenceService(IDbContextFactory cont .SetProperty(f => f.UpdatedBy, userId) .SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update evidence.")); })); public async Task> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) => diff --git a/PostFundManagement.Application/Services/Execution/ExecutionService.cs b/PostFundManagement.Application/Services/Execution/ExecutionService.cs index a548bb1..0d26dfb 100644 --- a/PostFundManagement.Application/Services/Execution/ExecutionService.cs +++ b/PostFundManagement.Application/Services/Execution/ExecutionService.cs @@ -59,7 +59,7 @@ public sealed class ExecutionService(IDbContextFactory con CreatedAt = DateTime.UtcNow }); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create risk.")); })); public async Task CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -80,7 +80,7 @@ public sealed class ExecutionService(IDbContextFactory con CreatedAt = DateTime.UtcNow }); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create disbursement.")); })); public async Task ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) => @@ -100,7 +100,7 @@ public sealed class ExecutionService(IDbContextFactory con .SetProperty(f => f.UpdatedBy, userId) .SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to approve disbursement.")); })); public async Task> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken) => @@ -117,8 +117,8 @@ public sealed class ExecutionService(IDbContextFactory 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 Result.FailIf(disbursement is null, NotFoundError.Create("Disbursement not found.")) + .ToResult(disbursement?.Map()!); })); public async Task> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) => @@ -135,8 +135,8 @@ public sealed class ExecutionService(IDbContextFactory 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 Result.FailIf(risk is null, NotFoundError.Create("Risk not found.")) + .ToResult(risk?.Map()!); })); public async Task UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) => @@ -152,7 +152,7 @@ public sealed class ExecutionService(IDbContextFactory con .SetProperty(u => u.Likelihood, request.Likelihood) .SetProperty(u => u.Impact, request.Impact), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update risk.")); })); public async Task UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -168,7 +168,7 @@ public sealed class ExecutionService(IDbContextFactory con .SetProperty(d => d.UpdatedAt, DateTime.UtcNow) .SetProperty(d => d.UpdatedBy, userId), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update disbursement.")); })); public async Task CreateRuleAsync(CreateRuleRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -231,8 +231,8 @@ public sealed class ExecutionService(IDbContextFactory con var rule = await context.Rules.AsNoTracking().FirstOrDefaultAsync(r => r.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(rule is null, NotFoundError.New) - .ToResult(rule!.Map()); + return Result.FailIf(rule is null, NotFoundError.Create("Rule not found.")) + .ToResult(rule?.Map()!); })); public async Task> GetRulesAsync(Pagination pagination, CancellationToken cancellationToken = default) => diff --git a/PostFundManagement.Application/Services/Governance/GovernanceService.cs b/PostFundManagement.Application/Services/Governance/GovernanceService.cs index d15825d..9fbc489 100644 --- a/PostFundManagement.Application/Services/Governance/GovernanceService.cs +++ b/PostFundManagement.Application/Services/Governance/GovernanceService.cs @@ -29,7 +29,7 @@ public sealed class GovernanceService(IDbContextFactory co .OrderByDescending(o => o.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(programmes.Select(p => p.Map()).ToArray()); + return Result.Ok(programmes?.Select(p => p.Map()).ToArray() ?? []); })); public async Task> GetOrganisationPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) => @@ -49,7 +49,7 @@ public sealed class GovernanceService(IDbContextFactory co .OrderByDescending(o => o.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(portfolios.Select(p => p.Map()).ToArray()); + return Result.Ok(portfolios?.Select(p => p.Map()).ToArray() ?? []); })); public async Task> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -64,7 +64,7 @@ public sealed class GovernanceService(IDbContextFactory co .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(organisations.Select(o => o.Organisation!.Map()).ToArray()); + return Result.Ok(organisations?.Select(o => o.Organisation!.Map()).ToArray() ?? []); })); public async Task> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) => @@ -82,8 +82,8 @@ public sealed class GovernanceService(IDbContextFactory co .Include(i => i.OrganisationId) .FirstOrDefaultAsync(o => o.OrganisationId == decodeResult.Value, cancellationToken); - return Result.FailIf(tenantOrganisation is null, NotFoundError.New) - .ToResult(tenantOrganisation!.Organisation!.Map()); + return Result.FailIf(tenantOrganisation is null, NotFoundError.Create("Organisation not found")) + .ToResult(tenantOrganisation?.Organisation?.Map()!); })); public async Task> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => @@ -102,7 +102,7 @@ public sealed class GovernanceService(IDbContextFactory co .OrderByDescending(o => o.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(instruments.Select(i => i.Map()).ToArray()); + return Result.Ok(instruments?.Select(i => i.Map()).ToArray() ?? []); })); public async Task> GetDashboardAsync(CancellationToken cancellationToken = default) => @@ -211,7 +211,7 @@ public sealed class GovernanceService(IDbContextFactory co TenantId = tenantContext.TenantId }); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create portfolio")); })); public async Task UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -248,7 +248,7 @@ public sealed class GovernanceService(IDbContextFactory co .OrderByDescending(o => o.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(programmes.Select(p => p.Map()).ToArray()); + return Result.Ok(programmes?.Select(p => p.Map()).ToArray() ?? []); })); public async Task> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) => @@ -265,8 +265,8 @@ public sealed class GovernanceService(IDbContextFactory co 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(portfolio is null, NotFoundError.Create("Portfolio not found")) + .ToResult(portfolio?.Map()!); })); public async Task CreateTenantAsync(CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -316,8 +316,8 @@ public sealed class GovernanceService(IDbContextFactory co var tenant = await context.Tenants.AsNoTracking().FirstOrDefaultAsync(t => t.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(tenant is null, NotFoundError.Create("Failed to retrieve tenants")) - .ToResult(tenant!.Map()); + return Result.FailIf(tenant is null, NotFoundError.Create("Failed to retrieve tenant")) + .ToResult(tenant?.Map()!); })); public async Task UpdateTenantAsync(UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -434,7 +434,7 @@ public sealed class GovernanceService(IDbContextFactory co .FirstOrDefaultAsync(f => f.TenantId == tenantContext.TenantId, cancellationToken); return Result.FailIf(tenantOrganisation is null, NotFoundError.Create("Failed to find tenant organisation map")) - .ToResult(tenantOrganisation!.Map()); + .ToResult(tenantOrganisation?.Map()!); })); public async Task UpdateInstrumentAsync(UpdateInstrumentRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -483,7 +483,7 @@ public sealed class GovernanceService(IDbContextFactory co .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(portfolios.Select(p => p.Map()).ToArray()); + return Result.Ok(portfolios?.Select(p => p.Map()).ToArray() ?? []); })); public async Task> GetProgrammeValueAsync(string programmeIdHash, CancellationToken cancellationToken = default) => diff --git a/PostFundManagement.Application/Services/Grants/GrantsService.cs b/PostFundManagement.Application/Services/Grants/GrantsService.cs index 9a108ba..59450b9 100644 --- a/PostFundManagement.Application/Services/Grants/GrantsService.cs +++ b/PostFundManagement.Application/Services/Grants/GrantsService.cs @@ -23,7 +23,7 @@ public sealed class GrantsService(IDbContextFactory contex .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(contracts.Select(c => c.Map()).ToArray()); + return Result.Ok(contracts?.Select(c => c.Map()).ToArray() ?? []); })); public async Task> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) => @@ -40,8 +40,8 @@ public sealed class GrantsService(IDbContextFactory 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 Result.FailIf(contract is null, NotFoundError.Create("Contract not found")) + .ToResult(contract?.Map()!); })); public async Task> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -55,7 +55,7 @@ public sealed class GrantsService(IDbContextFactory contex .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(budgets.Select(b => b.Map()).ToArray()); + return Result.Ok(budgets?.Select(b => b.Map()).ToArray() ?? []); })); public async Task> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) => @@ -72,8 +72,8 @@ public sealed class GrantsService(IDbContextFactory 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 Result.FailIf(budget is null, NotFoundError.Create("Budget not found")) + .ToResult(budget?.Map()!); })); public async Task> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -87,7 +87,7 @@ public sealed class GrantsService(IDbContextFactory contex .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(awards.Select(a => a.Map()).ToArray()); + return Result.Ok(awards?.Select(a => a.Map()).ToArray() ?? []); })); public async Task> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) => @@ -103,8 +103,8 @@ public sealed class GrantsService(IDbContextFactory 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 Result.FailIf(award is null, NotFoundError.Create("Award not found")) + .ToResult(award?.Map()!); })); public async Task ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -120,7 +120,7 @@ public sealed class GrantsService(IDbContextFactory 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.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.Create("Failed to update contract")); })); public async Task CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -147,7 +147,7 @@ public sealed class GrantsService(IDbContextFactory contex context.Contracts.Add(contract); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create contract")); })); public async Task CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -173,7 +173,7 @@ public sealed class GrantsService(IDbContextFactory contex context.Budgets.Add(budget); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create budget")); })); public async Task CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -203,7 +203,7 @@ public sealed class GrantsService(IDbContextFactory contex context.Awards.Add(award); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create award")); })); public async Task> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) => @@ -223,7 +223,7 @@ public sealed class GrantsService(IDbContextFactory contex return Result.Fail(NotFoundError.New); if (string.IsNullOrWhiteSpace(contract.SignedDocumentUrl)) - return Result.Fail(ValidationError.Create("Ecidence is invalid, has no saved documentUrl")); + return Result.Fail(ValidationError.Create("Evidence is invalid, has no saved documentUrl")); var secureDownloadUrlResult = await s3Service.GeneratePresignedUrlAsync(contract.SignedDocumentUrl, TimeSpan.FromMinutes(15), null, cancellationToken); diff --git a/PostFundManagement.Application/Services/Projects/ProjectService.cs b/PostFundManagement.Application/Services/Projects/ProjectService.cs index 6e6f3f0..47b8ac0 100644 --- a/PostFundManagement.Application/Services/Projects/ProjectService.cs +++ b/PostFundManagement.Application/Services/Projects/ProjectService.cs @@ -40,7 +40,7 @@ public sealed class ProjectService(IDbContextFactory conte .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(projects.Select(p => p.Map()).ToArray()); + return Result.Ok(projects?.Select(p => p.Map()).ToArray() ?? []); })); public async Task> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) => @@ -57,8 +57,8 @@ public sealed class ProjectService(IDbContextFactory 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 Result.FailIf(project is null, NotFoundError.Create("Project not found")) + .ToResult(project?.Map()!); })); public async Task> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) => @@ -76,7 +76,7 @@ public sealed class ProjectService(IDbContextFactory conte .OrderByDescending(a => a.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(activities.Select(a => a.Map()).ToArray()); + return Result.Ok(activities?.Select(a => a.Map()).ToArray() ?? []); })); public async Task CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -157,7 +157,7 @@ public sealed class ProjectService(IDbContextFactory conte .OrderBy(m => m.DueAt) .ToListAsync(cancellationToken); - return Result.Ok(milestones.Select(i => i.Map()).ToArray()); + return Result.Ok(milestones?.Select(i => i.Map()).ToArray() ?? []); })); public async Task> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken) => @@ -176,7 +176,7 @@ public sealed class ProjectService(IDbContextFactory conte .OrderByDescending(o => o.Id) .ToListAsync(cancellationToken); - return Result.Ok(evidences.Select(e => e.Map()).ToArray()); + return Result.Ok(evidences?.Select(e => e.Map()).ToArray() ?? []); })); public async Task> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) => @@ -192,8 +192,8 @@ public sealed class ProjectService(IDbContextFactory 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 Result.FailIf(milestone is null, NotFoundError.Create("Milestone not found")) + .ToResult(milestone?.Map()!); })); public async Task> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -207,7 +207,7 @@ public sealed class ProjectService(IDbContextFactory conte .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(milestones.Select(i => i.Map()).ToArray()); + return Result.Ok(milestones?.Select(i => i.Map()).ToArray() ?? []); })); public async Task UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -216,7 +216,7 @@ public sealed class ProjectService(IDbContextFactory conte await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken)) - return Result.Fail(NotFoundError.New); + return Result.Fail(NotFoundError.Create("User not found")); var changedTotal = await context.Milestones.Where(m => m.Id == request.MilestoneId) .ExecuteUpdateAsync(f => f @@ -263,8 +263,8 @@ public sealed class ProjectService(IDbContextFactory 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 Result.FailIf(activity is null, NotFoundError.Create("Activity not found")) + .ToResult(activity?.Map()!); })); public async Task> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) => @@ -283,7 +283,7 @@ public sealed class ProjectService(IDbContextFactory conte .OrderByDescending(r => r.CreatedAt) .ToListAsync(cancellationToken); - return Result.Ok(risks.Select(i => i.Map()).ToArray()); + return Result.Ok(risks?.Select(i => i.Map()).ToArray() ?? []); })); public async Task CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -310,7 +310,7 @@ public sealed class ProjectService(IDbContextFactory conte TenantId = tenantContext.TenantId }); - return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New); + return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create programme.")); })); public async Task UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -319,7 +319,7 @@ public sealed class ProjectService(IDbContextFactory conte await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken)) - return Result.Fail(NotFoundError.New); + return Result.Fail(NotFoundError.Create("User not found")); var changedTotal = await context.Programmes.Where(p => p.Id == request.ProgrammeId) .ExecuteUpdateAsync(f => f @@ -344,8 +344,8 @@ public sealed class ProjectService(IDbContextFactory conte var programme = await context.Programmes.FirstOrDefaultAsync(p => p.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(programme is null, NotFoundError.New) - .ToResult(programme!.Map()); + return Result.FailIf(programme is null, NotFoundError.Create("Programme not found")) + .ToResult(programme?.Map()!); })); public async Task> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) => @@ -359,7 +359,7 @@ public sealed class ProjectService(IDbContextFactory conte .Take(pagination.PageSize) .ToListAsync(cancellationToken); - return Result.Ok(programmes.Select(i => i.Map()).ToArray()); + return Result.Ok(programmes?.Select(i => i.Map()).ToArray() ?? []); })); public async Task> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) => @@ -373,14 +373,14 @@ public sealed class ProjectService(IDbContextFactory conte await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); if (!await context.Programmes.AnyAsync(p => p.Id == decodeResult.Value, cancellationToken)) - return Result.Fail(NotFoundError.New); + return Result.Fail(NotFoundError.Create("Programme not found")); 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()); + return Result.Ok(projects?.Select(i => i.Map()).ToArray() ?? []); })); public async Task CreateChangeRequestAsync(CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -456,8 +456,8 @@ public sealed class ProjectService(IDbContextFactory conte var changeRequest = await context.ChangeRequests.AsNoTracking().FirstOrDefaultAsync(c => c.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(changeRequest is null, NotFoundError.New) - .ToResult(changeRequest!.Map()); + return Result.FailIf(changeRequest is null, NotFoundError.Create("Change request not found")) + .ToResult(changeRequest?.Map()!); })); public async Task CreateComplianceItemAsync(CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -529,8 +529,8 @@ public sealed class ProjectService(IDbContextFactory conte var compliancItem = await context.ComplianceItems.AsNoTracking().FirstOrDefaultAsync(c => c.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(compliancItem is null, NotFoundError.New) - .ToResult(compliancItem!.Map()); + return Result.FailIf(compliancItem is null, NotFoundError.Create("Compliance item not found")) + .ToResult(compliancItem?.Map()!); })); public async Task CreateBeneficiaryAsync(CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -606,8 +606,8 @@ public sealed class ProjectService(IDbContextFactory conte var beneficiary = await context.Beneficiaries.AsNoTracking() .FirstOrDefaultAsync(b => b.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(beneficiary is null, NotFoundError.New) - .ToResult(beneficiary!.Map()); + return Result.FailIf(beneficiary is null, NotFoundError.Create("Beneficiary not found")) + .ToResult(beneficiary?.Map()!); })); public async Task CreateIssueAsync(CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -681,8 +681,8 @@ public sealed class ProjectService(IDbContextFactory conte var issue = await context.Issues.AsNoTracking().FirstOrDefaultAsync(i => i.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(issue is null, NotFoundError.New) - .ToResult(issue!.Map()); + return Result.FailIf(issue is null, NotFoundError.Create("Issue not found")) + .ToResult(issue?.Map()!); })); public async Task CreateOutcomeAsync(CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -750,8 +750,8 @@ public sealed class ProjectService(IDbContextFactory conte var outcome = await context.Outcomes.AsNoTracking() .FirstOrDefaultAsync(o => o.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(outcome is null, NotFoundError.New) - .ToResult(outcome!.Map()); + return Result.FailIf(outcome is null, NotFoundError.Create("Outcome not found")) + .ToResult(outcome?.Map()!); })); public async Task CreateSiteVisitAsync(CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) => @@ -820,7 +820,7 @@ public sealed class ProjectService(IDbContextFactory conte var visit = await context.SiteVisits.AsNoTracking().FirstOrDefaultAsync(v => v.Id == decodeResult.Value, cancellationToken); - return Result.FailIf(visit is null, NotFoundError.New) - .ToResult(visit!.Map()); + return Result.FailIf(visit is null, NotFoundError.Create("Site visit not found")) + .ToResult(visit?.Map()!); })); } diff --git a/PostFundManagement.Application/Services/Users/UserService.cs b/PostFundManagement.Application/Services/Users/UserService.cs index 996372e..755ddc6 100644 --- a/PostFundManagement.Application/Services/Users/UserService.cs +++ b/PostFundManagement.Application/Services/Users/UserService.cs @@ -98,7 +98,7 @@ public sealed class UserService(IDbContextFactory contextF changedTotal += await context.Users.Where(f => f.Id == userId) .ExecuteUpdateAsync(f => f.SetProperty(u => u.Status, status), cancellationToken); - return Result.OkIf(changedTotal > 0, UpdateError.New); + return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update user status.")); })); public async Task> GetUserAsync(Guid userId, CancellationToken cancellationToken) => @@ -110,7 +110,7 @@ public sealed class UserService(IDbContextFactory contextF .Include(i => i.User) .FirstOrDefaultAsync(f => f.UserId == userId, cancellationToken); - return Result.FailIf(tenantUser == null, NotFoundError.New) + return Result.FailIf(tenantUser == null, NotFoundError.Create("User not found.")) .ToResult(tenantUser!.User?.Map()!); })); } diff --git a/PostFundManagement.Domain/Abstractions/ITenantContextService.cs b/PostFundManagement.Domain/Abstractions/ITenantContextService.cs index 21a94cc..fa1cc97 100644 --- a/PostFundManagement.Domain/Abstractions/ITenantContextService.cs +++ b/PostFundManagement.Domain/Abstractions/ITenantContextService.cs @@ -4,6 +4,7 @@ public interface ITenantContextService { string? TenantName { get; } long TenantId { get; } + bool ByPassTenantQueryFilter { get; set; } bool IsAdmin { get; } bool HasTenantFilter { get; } } diff --git a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs index 658d995..8e8415d 100644 --- a/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs +++ b/PostFundManagement.Infrastructure/Database/ApplicationDbContext.cs @@ -6,7 +6,7 @@ namespace PostFundManagement.Infrastructure.Database; public sealed class ApplicationDbContext(DbContextOptions options, ITenantContextService tenantService) : DbContext(options) { - private bool BypassFilter => tenantService.IsAdmin; + private bool BypassFilter => tenantService.IsAdmin || tenantService.ByPassTenantQueryFilter; private long CurrentTenantId => tenantService.TenantId; public DbSet TenantUsers => Set(); @@ -67,7 +67,7 @@ public sealed class ApplicationDbContext(DbContextOptions private void ApplyGlobalTenantFilter(ModelBuilder modelBuilder) where TEntity : class, ITenantEntity => - modelBuilder.Entity().HasQueryFilter(e => BypassFilter || e.TenantId == CurrentTenantId); + modelBuilder.Entity().HasQueryFilter(e => BypassFilter == false && e.TenantId == CurrentTenantId); protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -84,14 +84,14 @@ public sealed class ApplicationDbContext(DbContextOptions } } - modelBuilder.Entity().HasQueryFilter(o => BypassFilter || + modelBuilder.Entity().HasQueryFilter(o => BypassFilter == false && Set().Any(to => to.TenantId == CurrentTenantId && to.OrganisationId == o.Id)); modelBuilder.Entity() - .HasQueryFilter(to => BypassFilter || to.TenantId == CurrentTenantId); + .HasQueryFilter(to => BypassFilter == false && to.TenantId == CurrentTenantId); modelBuilder.Entity() - .HasQueryFilter(to => BypassFilter || to.TenantId == CurrentTenantId); + .HasQueryFilter(to => BypassFilter == false && to.TenantId == CurrentTenantId); } public override Task SaveChangesAsync(CancellationToken cancellationToken = default) diff --git a/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs b/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs index 66c1707..7dc112a 100644 --- a/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs +++ b/PostFundManagement.Infrastructure/Database/Services/DesignTimeTenantContextService.cs @@ -10,5 +10,6 @@ public sealed partial class ApplicationDbContextFactory public long TenantId => 0L; public bool IsAdmin => true; public bool HasTenantFilter => false; + public bool ByPassTenantQueryFilter { get; set; } = true; } } \ No newline at end of file diff --git a/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs b/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs index 9eae36f..cf81640 100644 --- a/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs +++ b/PostFundManagement.Infrastructure/Database/Services/TenantContextService.cs @@ -8,6 +8,8 @@ public sealed class TenantContextService : ITenantContextService public long TenantId { get; } + public bool ByPassTenantQueryFilter { get; set; } = false; + public bool IsAdmin { get; } public bool HasTenantFilter { get; } -- 2.47.3