Refactored endpoints to be more functional and more expressive

This commit is contained in:
2026-09-01 11:23:25 +02:00
parent 346cb8da5f
commit 2df5b5c2ed
86 changed files with 1565 additions and 1575 deletions
@@ -1,4 +1,5 @@
using PostFundManagement.Application.Shared;
using PostFundManagement.Application.Services.Shared;
using PostFundManagement.Domain.Errors;
namespace PostFundManagement.Application.Abstractions;
@@ -9,17 +10,16 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
protected abstract string BucketName { get; }
protected abstract string CdnBaseUrl { get; }
public virtual async Task<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default)
{
try
public virtual async ValueTask<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default) =>
await Result.Try((Func<ValueTask<Result<string>>>)(async () =>
{
if (string.IsNullOrWhiteSpace(BucketName))
return Result.Fail<string>("Bucket name is not configured.");
return Result.Fail<string>(Domain.Errors.ValidationError.Create("Bucket name is not configured."));
if (string.IsNullOrWhiteSpace(CdnBaseUrl))
return Result.Fail<string>("CDN base URL is not configured.");
return Result.Fail<string>(Domain.Errors.ValidationError.Create("CDN base URL is not configured."));
using var stream = new MemoryStream();
await using var stream = new MemoryStream();
await fileStream.CopyToAsync(stream, cancellationToken);
await fileStream.DisposeAsync();
@@ -28,8 +28,8 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
var fileHash = HashService.StreamToSha256Hash(stream);
if(string.IsNullOrWhiteSpace(fileHash))
return Result.Fail<string>("Failed to compute file hash.");
if (string.IsNullOrWhiteSpace(fileHash))
return Result.Fail<string>(CreateError.Create("Failed to compute file hash."));
var fileKey = $"{fileHash.ToLower(CultureInfo.InvariantCulture)}{Path.GetExtension(fileName)}";
@@ -46,38 +46,24 @@ public abstract class S3ServiceBase(IAmazonS3 amazonS3)
var response = await Client.PutObjectAsync(putRequest, cancellationToken);
return response.HttpStatusCode != System.Net.HttpStatusCode.OK
? Result.Fail<string>($"Failed to upload {fileName} to S3.")
return response.HttpStatusCode != HttpStatusCode.OK
? Result.Fail<string>(CreateError.Create($"Failed to upload {fileName} to S3."))
: Result.Ok($"{CdnBaseUrl}/{fileKey}");
}
catch (Exception ex)
{
return Result.Fail<string>(new Error($"Error uploading {fileName} to S3: {ex.Message}").CausedBy(ex));
}
}
}));
public virtual async Task<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default)
{
try
public virtual async ValueTask<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default) =>
await Result.Try((Func<ValueTask<Result>>)(async () =>
{
if (string.IsNullOrWhiteSpace(BucketName))
return Result.Fail("Bucket name is not configured.");
return Result.Fail(NotFoundError.Create("Bucket name is not configured."));
var deleteRequest = new DeleteObjectRequest
var response = await Client.DeleteObjectAsync(new DeleteObjectRequest
{
BucketName = BucketName,
Key = fileKey
};
}, cancellationToken);
var response = await Client.DeleteObjectAsync(deleteRequest, cancellationToken);
return response.HttpStatusCode != System.Net.HttpStatusCode.NoContent
? Result.Fail($"Failed to delete {fileKey} from S3.")
: Result.Ok();
}
catch (Exception ex)
{
return Result.Fail(new Error($"Error deleting {fileKey} from S3: {ex.Message}").CausedBy(ex));
}
}
return Result.FailIf(response.HttpStatusCode == HttpStatusCode.NoContent,
UpdateError.Create($"Failed to delete {fileKey} from S3."));
}));
}