Hardened API and catered for Partner role query filter bypass

This commit is contained in:
2026-10-04 08:18:43 +02:00
parent 8d069c2da0
commit 8100518549
62 changed files with 232 additions and 193 deletions
@@ -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);