Hardened API and catered for Partner role query filter bypass
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<object?> 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);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
|
||||
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0 && notificationResult.IsFailed,
|
||||
CreateError.Create("Failed to create invite, contact support").CausedBy(notificationResult.Errors))
|
||||
.ToResult(invite.Map());
|
||||
.ToResult(invite?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Invite[]>> GetInvitesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -72,8 +72,8 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
.OrderByDescending(o => 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<Result<Invite[]>> GetApplicantInvitesAsync(Guid applicantUserId, CancellationToken cancellationToken = default) =>
|
||||
@@ -86,7 +86,7 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
.OrderByDescending(o => 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<Result> CreateNotificationAsync(CreateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -162,6 +162,6 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
|
||||
.FirstOrDefaultAsync(n => n.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(notification is null, NotFoundError.Create("Notification not found"))
|
||||
.ToResult(notification!.Map());
|
||||
.ToResult(notification?.Map()!);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result> RecordIndicatorActualAmountAsync(string indicatorIdHash, decimal amount, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -65,7 +65,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
.SetProperty(f => f.ActualAmount, amount)
|
||||
.SetProperty(f => f.CreatedBy, userId), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update indicator."));
|
||||
}));
|
||||
|
||||
public async Task<Result<Indicator[]>> GetIndicatorsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -79,7 +79,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result<Domain.Models.Evidence[]>> GetEvidenceFilesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -93,7 +93,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateMilestoneAsync(CreateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -142,7 +142,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result<Indicator>> GetIndicatorAsync(string indicatorId, CancellationToken cancellationToken) =>
|
||||
@@ -158,8 +158,8 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
|
||||
var indicator = await context.Indicators.AsNoTracking().FirstOrDefaultAsync(i => i.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(indicator is null, NotFoundError.New)
|
||||
.ToResult(indicator!.Map());
|
||||
return Result.FailIf(indicator is null, NotFoundError.Create("Indicator not found."))
|
||||
.ToResult(indicator?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Domain.Models.Evidence>> GetEvidenceAsync(string evidenceId, CancellationToken cancellationToken = default) =>
|
||||
@@ -175,8 +175,8 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
|
||||
var evidence = await context.Evidences.AsNoTracking().FirstOrDefaultAsync(e => e.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(evidence is null, NotFoundError.New)
|
||||
.ToResult(evidence!.Map());
|
||||
return Result.FailIf(evidence is null, NotFoundError.Create("Evidence not found."))
|
||||
.ToResult(evidence?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Domain.Models.Evidence[]>> GetMilestoneEvidencesAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -196,7 +196,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result<Domain.Models.Evidence[]>> GetProjectEvidencesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -216,7 +216,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdateEvidenceAsync(UpdateEvidenceRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -235,7 +235,7 @@ public sealed class EvidenceService(IDbContextFactory<ApplicationDbContext> cont
|
||||
.SetProperty(f => f.UpdatedBy, userId)
|
||||
.SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update evidence."));
|
||||
}));
|
||||
|
||||
public async Task<Result<string>> GetEvidenceUrlAsync(string evidenceIdHash, CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateDisbursementAsync(CreateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -80,7 +80,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> 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<Result> ApproveDisbursementAsync(string disbursementIdHash, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -100,7 +100,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(f => f.UpdatedBy, userId)
|
||||
.SetProperty(f => f.UpdatedAt, DateTime.UtcNow), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to approve disbursement."));
|
||||
}));
|
||||
|
||||
public async Task<Result<Disbursement>> GetDisbursementAsync(string disbursementIdHash, CancellationToken cancellationToken) =>
|
||||
@@ -117,8 +117,8 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
var disbursement = await context.Disbursements.AsNoTracking()
|
||||
.FirstOrDefaultAsync(d => d.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(disbursement is null, NotFoundError.New)
|
||||
.ToResult(disbursement!.Map());
|
||||
return Result.FailIf(disbursement is null, NotFoundError.Create("Disbursement not found."))
|
||||
.ToResult(disbursement?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Risk>> GetRiskAsync(string riskIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -135,8 +135,8 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
var risk = await context.Risks.AsNoTracking()
|
||||
.FirstOrDefaultAsync(r => r.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(risk is null, NotFoundError.New)
|
||||
.ToResult(risk!.Map());
|
||||
return Result.FailIf(risk is null, NotFoundError.Create("Risk not found."))
|
||||
.ToResult(risk?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result> UpdateRiskAsync(UpdateRiskRequest request, CancellationToken cancellationToken = default) =>
|
||||
@@ -152,7 +152,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(u => u.Likelihood, request.Likelihood)
|
||||
.SetProperty(u => u.Impact, request.Impact), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update risk."));
|
||||
}));
|
||||
|
||||
public async Task<Result> UpdateDisbursementAsync(UpdateDisbursementRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -168,7 +168,7 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> con
|
||||
.SetProperty(d => d.UpdatedAt, DateTime.UtcNow)
|
||||
.SetProperty(d => d.UpdatedBy, userId), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.New);
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update disbursement."));
|
||||
}));
|
||||
|
||||
public async Task<Result> CreateRuleAsync(CreateRuleRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -231,8 +231,8 @@ public sealed class ExecutionService(IDbContextFactory<ApplicationDbContext> 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<Result<Rule[]>> GetRulesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Portfolio[]>> GetOrganisationPorfoliosAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -49,7 +49,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Organisation[]>> GetOrganisationsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -64,7 +64,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Organisation>> GetOrganisationAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -82,8 +82,8 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Instrument[]>> GetInstrumentsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -102,7 +102,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Dashboard>> GetDashboardAsync(CancellationToken cancellationToken = default) =>
|
||||
@@ -211,7 +211,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdatePortfolioAsync(UpdatePortfolioRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -248,7 +248,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<Portfolio>> GetPortfolioAsync(string portfolioIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -265,8 +265,8 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateTenantAsync(CreateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -316,8 +316,8 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdateTenantAsync(UpdateTenantRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -434,7 +434,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdateInstrumentAsync(UpdateInstrumentRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -483,7 +483,7 @@ public sealed class GovernanceService(IDbContextFactory<ApplicationDbContext> 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<Result<decimal>> GetProgrammeValueAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
|
||||
@@ -23,7 +23,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> 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<Result<Contract>> GetContractAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -40,8 +40,8 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
var contract = await context.Contracts.AsNoTracking()
|
||||
.FirstOrDefaultAsync(c => c.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(contract is null, NotFoundError.New)
|
||||
.ToResult(contract!.Map());
|
||||
return Result.FailIf(contract is null, NotFoundError.Create("Contract not found"))
|
||||
.ToResult(contract?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Budget[]>> GetBudgetsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -55,7 +55,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> 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<Result<Budget>> GetBudgetAsync(string budgetIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -72,8 +72,8 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
var budget = await context.Budgets.AsNoTracking()
|
||||
.FirstOrDefaultAsync(b => b.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(budget is null, NotFoundError.New)
|
||||
.ToResult(budget!.Map());
|
||||
return Result.FailIf(budget is null, NotFoundError.Create("Budget not found"))
|
||||
.ToResult(budget?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Award[]>> GetAwardsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -87,7 +87,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> 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<Result<Award>> GetAwardAsync(string awardIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -103,8 +103,8 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
var award = await context.Awards.AsNoTracking().FirstOrDefaultAsync(a => a.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(award is null, NotFoundError.New)
|
||||
.ToResult(award!.Map());
|
||||
return Result.FailIf(award is null, NotFoundError.Create("Award not found"))
|
||||
.ToResult(award?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result> ExecuteContractAsync(ExecuteContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -120,7 +120,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
.SetProperty(u => u.CreatedBy, userId)
|
||||
.SetProperty(u => u.Status, ContractStatus.Active), cancellationToken);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.New);
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, UpdateError.Create("Failed to update contract"));
|
||||
}));
|
||||
|
||||
public async Task<Result> CreateContractAsync(CreateContractRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -147,7 +147,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateBudgetAsync(CreateBudgetRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -173,7 +173,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
context.Budgets.Add(budget);
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create budget"));
|
||||
}));
|
||||
|
||||
public async Task<Result> CreateAwardAsync(CreateAwardRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -203,7 +203,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> 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<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -223,7 +223,7 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
return Result.Fail<string>(NotFoundError.New);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(contract.SignedDocumentUrl))
|
||||
return Result.Fail<string>(ValidationError.Create("Ecidence is invalid, has no saved documentUrl"));
|
||||
return Result.Fail<string>(ValidationError.Create("Evidence is invalid, has no saved documentUrl"));
|
||||
|
||||
var secureDownloadUrlResult = await s3Service.GeneratePresignedUrlAsync(contract.SignedDocumentUrl,
|
||||
TimeSpan.FromMinutes(15), null, cancellationToken);
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -57,8 +57,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
var project = await context.Projects.AsNoTracking()
|
||||
.FirstOrDefaultAsync(p => p.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(project is null, NotFoundError.New)
|
||||
.ToResult(project!.Map());
|
||||
return Result.FailIf(project is null, NotFoundError.Create("Project not found"))
|
||||
.ToResult(project?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Domain.Models.Activity[]>> GetActivitiesAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -76,7 +76,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateProjectAsync(CreateProjectRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -157,7 +157,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result<Indicator[]>> GetProjectIndicatorsAsync(string projectIdHash, CancellationToken cancellationToken) =>
|
||||
@@ -176,7 +176,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result<Milestone>> GetMilestoneAsync(string milestoneIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -192,8 +192,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> conte
|
||||
var milestone = await context.Milestones.AsNoTracking()
|
||||
.FirstOrDefaultAsync(m => m.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(milestone is null, NotFoundError.New)
|
||||
.ToResult(milestone!.Map());
|
||||
return Result.FailIf(milestone is null, NotFoundError.Create("Milestone not found"))
|
||||
.ToResult(milestone?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Milestone[]>> GetMilestonesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -207,7 +207,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdateMilestoneAsync(UpdateMilestoneRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -216,7 +216,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<ApplicationDbContext> conte
|
||||
var activity = await context.Activities.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
return Result.FailIf(activity is null, NotFoundError.New)
|
||||
.ToResult(activity!.Map());
|
||||
return Result.FailIf(activity is null, NotFoundError.Create("Activity not found"))
|
||||
.ToResult(activity?.Map()!);
|
||||
}));
|
||||
|
||||
public async Task<Result<Risk[]>> GetProjectRisksAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -283,7 +283,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateProgrammeAsync(CreateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -310,7 +310,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> UpdateProgrammeAsync(UpdateProgrammeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -319,7 +319,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<ApplicationDbContext> 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<Result<Programme[]>> GetProgrammesAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
@@ -359,7 +359,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result<Project[]>> GetProgrammeProjectsAsync(string programmeIdHash, CancellationToken cancellationToken = default) =>
|
||||
@@ -373,14 +373,14 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateChangeRequestAsync(CreateChangeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -456,8 +456,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateComplianceItemAsync(CreateComplianceItemRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -529,8 +529,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateBeneficiaryAsync(CreateBeneficiaryRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -606,8 +606,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateIssueAsync(CreateIssueRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -681,8 +681,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateOutcomeAsync(CreateOutcomeRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -750,8 +750,8 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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<Result> CreateSiteVisitAsync(CreateSiteVisitRequest request, Guid userId, CancellationToken cancellationToken = default) =>
|
||||
@@ -820,7 +820,7 @@ public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> 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()!);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> 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<Result<User>> GetUserAsync(Guid userId, CancellationToken cancellationToken) =>
|
||||
@@ -110,7 +110,7 @@ public sealed class UserService(IDbContextFactory<ApplicationDbContext> 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()!);
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user