Implemented missing file handling endpoints
Added contract update endpoint
This commit is contained in:
@@ -146,4 +146,31 @@ public sealed class GrantsCacheService(GrantsService service, IDistributedCache
|
||||
|
||||
return Result.Ok(cached.FromJson<Contract[]>());
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
|
||||
{
|
||||
var cacheKey = HashService.ToMd5Hash($"getcontracturl-{contractIdHash}").Value;
|
||||
|
||||
var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(cached))
|
||||
{
|
||||
var result = await service.GetContractUrlAsync(contractIdHash, cancellationToken);
|
||||
|
||||
if(result.IsFailed) return result;
|
||||
|
||||
await cache.SetStringAsync(cacheKey, result.Value, cancellationToken);
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
return Result.Ok(cached!);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) =>
|
||||
await service.UpdateContractAsync(request, cancellationToken);
|
||||
|
||||
public async ValueTask<Result> UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) =>
|
||||
await service.UploadContractAsync(contractIdHash, userId, file, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Shared;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Domain.Errors;
|
||||
using PostFundManagement.Domain.Extensions;
|
||||
using PostFundManagement.Domain.Models;
|
||||
@@ -8,7 +9,8 @@ using PostFundManagement.Infrastructure.Database;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService) : IGrantsService
|
||||
public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService,
|
||||
[FromKeyedServices(Constants.GeneralBucketName)] IS3Service s3Service) : IGrantsService
|
||||
{
|
||||
public async ValueTask<Result<Contract[]>> GetContractsAsync(Pagination pagination, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<Contract[]>>>)(async () =>
|
||||
@@ -201,4 +203,73 @@ public sealed class GrantsService(IDbContextFactory<ApplicationDbContext> contex
|
||||
|
||||
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.New);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result<string>> GetContractUrlAsync(string contractIdHash, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(contractIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var contract = await context.Contracts.AsNoTracking().FirstOrDefaultAsync(e => e.Id == decodeResult.Value, cancellationToken);
|
||||
|
||||
if (contract is null)
|
||||
return Result.Fail<string>(NotFoundError.New);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(contract.SignedDocumentUrl))
|
||||
return Result.Fail<string>(ValidationError.Create("Ecidence is invalid, has no saved documentUrl"));
|
||||
|
||||
var secureDownloadUrlResult = await s3Service.GeneratePresignedUrlAsync(contract.SignedDocumentUrl,
|
||||
TimeSpan.FromMinutes(15), null, cancellationToken);
|
||||
|
||||
return Result.OkIf(secureDownloadUrlResult.IsSuccess && !string.IsNullOrWhiteSpace(secureDownloadUrlResult.Value),
|
||||
NotFoundError.Create("Failed to generate secure file url"))
|
||||
.ToResult(secureDownloadUrlResult.Value);
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UploadContractAsync(string contractIdHash, Guid userId, IFormFile file, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
var decodeResult = hashService.DecodeLongIdHash(contractIdHash);
|
||||
|
||||
if (decodeResult.IsFailed)
|
||||
return Result.Fail(IdDecodeError.Create()
|
||||
.CausedBy(decodeResult.Errors));
|
||||
|
||||
await using var stream = file.OpenReadStream();
|
||||
|
||||
var uploadResult = await s3Service.UploadFileAsync(file.FileName, "contracts", stream,
|
||||
file.ContentType, cancellationToken);
|
||||
|
||||
if (uploadResult.IsFailed)
|
||||
return Result.Fail(uploadResult.Errors);
|
||||
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var changedTotal = await context.Contracts.Where(c => c.Id == decodeResult.Value)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(c => c.SignedDocumentUrl, uploadResult.Value), cancellationToken);
|
||||
|
||||
var secureDownloadUrlResult = await s3Service.GeneratePresignedUrlAsync(uploadResult.Value,
|
||||
TimeSpan.FromMinutes(15), null, cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0 && secureDownloadUrlResult.IsSuccess && !string.IsNullOrWhiteSpace(secureDownloadUrlResult.Value),
|
||||
CreateError.Create("Failed to upload contract file"));
|
||||
}));
|
||||
|
||||
public async ValueTask<Result> UpdateContractAsync(UpdateContractRequest request, CancellationToken cancellationToken = default) =>
|
||||
await Result.Try((Func<ValueTask<Result>>)(async () =>
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var changedTotal = await context.Contracts.Where(c => c.Id == request.ContractId)
|
||||
.ExecuteUpdateAsync(f => f
|
||||
.SetProperty(u => u.Status, request.Status), cancellationToken);
|
||||
|
||||
return Result.OkIf(changedTotal > 0, UpdateError.Create("Failed to update the contract."));
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
using PostFundManagement.Domain;
|
||||
using static PostFundManagement.Domain.Extensions.Constants;
|
||||
|
||||
namespace PostFundManagement.Application.Services.Grants;
|
||||
|
||||
public record CreateAwardRequest([Required] long OrganisationId, [Required] long ProgrammeId, [Required] decimal Amount);
|
||||
public sealed record UpdateContractRequest([Required] long ContractId, [Required] ContractStatus Status);
|
||||
|
||||
public record CreateBudgetRequest([Required] long ProjectId,
|
||||
public sealed record CreateAwardRequest([Required] long OrganisationId, [Required] long ProgrammeId, [Required] decimal Amount);
|
||||
|
||||
public sealed record CreateBudgetRequest([Required] long ProjectId,
|
||||
[Required][MaxLength(LabelLength)] string Name,
|
||||
[Required] decimal Amount);
|
||||
|
||||
public record CreateContractRequest([Required] long AwardId,
|
||||
public sealed record CreateContractRequest([Required] long AwardId,
|
||||
[Required] decimal TotalValue,
|
||||
[Required] DateTime EffectiveAt,
|
||||
[Required] DateTime ExpiresAt);
|
||||
|
||||
public record ExecuteContractRequest([Required] long ContractId,
|
||||
public sealed record ExecuteContractRequest([Required] long ContractId,
|
||||
[Required][MaxLength(LabelLength)] string ExternalSignatureId,
|
||||
[Required][MaxLength(LabelLength)] string SignedDocumentUrl);
|
||||
|
||||
Reference in New Issue
Block a user