Skip to content

Commit d819d91

Browse files
committed
fix: Correção na feature de deletar imagens na Bucket
Bom foi adaptado para remover mais de uuma imagem de uma vez, além de salvar como deletada no banco que ficou faltando na primeira versão do CleanupImageBucketJob. Também foi feita a correção nos testes para ficar de acordo com a nova versão do método de deletar imagem.
1 parent b99578b commit d819d91

File tree

9 files changed

+513
-302
lines changed

9 files changed

+513
-302
lines changed

src/Riber.Application/Abstractions/Services/IImageStorageService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ public interface IImageStorageService
2323
Task<Stream> GetImageStreamAsync(string fileName);
2424

2525
/// <summary>
26-
/// Remove uma imagem do serviço de armazenamento.
26+
/// Remove uma ou mais imagens do serviço de armazenamento.
2727
/// </summary>
28-
/// <param name="fileName">O nome do arquivo da imagem a ser removida.</param>
29-
/// <returns>Uma task representando a operação assíncrona.</returns>
30-
Task DeleteAsync(string fileName);
28+
/// <param name="fileKeys">As chaves dos arquivos das imagens que devem ser removidas.</param>
29+
/// <returns>Um IEnumerable contendo as chaves dos arquivos que foram excluídos.</returns>
30+
Task<IEnumerable<string>> DeleteAllAsync(List<string> fileKeys);
3131
}

src/Riber.Domain/Repositories/IProductRepository.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,10 @@ Task<IEnumerable<ProductCategory>> GetCategoriesAsync(
8080
/// <param name="cancellationToken">Token para cancelamento da operação assíncrona.</param>
8181
/// <returns>Uma coleção de imagens não utilizadas.</returns>
8282
Task<IReadOnlyList<Image>> GetUnusedImagesAsync(CancellationToken cancellationToken = default);
83+
84+
/// <summary>
85+
/// Remove uma imagem específica do serviço de armazenamento.
86+
/// </summary>
87+
/// <param name="image">A imagem que deve ser removida.</param>
88+
void DeleteImage(Image image);
8389
}

src/Riber.Infrastructure/BackgroundJobs/CleanupImageBucketJob.cs

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,45 @@ namespace Riber.Infrastructure.BackgroundJobs;
88

99
internal sealed class CleanupImageBucketJob(
1010
IImageStorageService imageStorageService,
11-
IProductRepository productRepository,
11+
IUnitOfWork unitOfWork,
1212
ILogger<CleanupImageBucketJob> logger)
1313
: IJob
1414
{
1515
public async Task Execute(IJobExecutionContext context)
1616
{
17-
var unusedImages = await productRepository.GetUnusedImagesAsync(context.CancellationToken);
18-
if (unusedImages.Count == 0)
17+
try
1918
{
20-
logger.LogInformation("No unused images found for cleanup.");
21-
return;
22-
}
19+
var unusedImages = await unitOfWork.Products.GetUnusedImagesAsync(context.CancellationToken);
20+
if (unusedImages.Count == 0)
21+
{
22+
logger.LogInformation("No unused images found for cleanup.");
23+
return;
24+
}
2325

24-
logger.LogInformation("Starting cleanup of {Count} unused images.", unusedImages.Count);
25-
int deletedCount = 0;
26-
int failedCount = 0;
26+
logger.LogInformation("Starting cleanup of {Count} unused images.", unusedImages.Count);
27+
var unusedImageKeys = unusedImages.Select(x => x.ToString()).ToList();
2728

28-
foreach (Image unusedImage in unusedImages)
29+
var deletedKeys = (await imageStorageService.DeleteAllAsync(unusedImageKeys)).ToHashSet();
30+
await MarkImageAsDeletedAsync(unusedImages, deletedKeys);
31+
}
32+
catch (Exception ex)
2933
{
30-
try
31-
{
32-
await imageStorageService.DeleteAsync(unusedImage);
33-
deletedCount++;
34-
logger.LogDebug("Successfully deleted image {ImageId}.", unusedImage.Id);
35-
}
36-
catch (Exception ex)
37-
{
38-
failedCount++;
39-
logger.LogError(ex,
40-
"Failed to delete image {ImageId} - {ImageName}.",
41-
unusedImage.Id,
42-
unusedImage.ToString()
43-
);
44-
}
34+
logger.LogError(ex, "Failed to delete images from storage service.");
4535
}
36+
}
37+
38+
private async Task MarkImageAsDeletedAsync(IReadOnlyList<Image> unusedImage, HashSet<string> deletedKeys)
39+
{
40+
var imagesForDeletion = unusedImage
41+
.Where(image => deletedKeys.Contains(image.ToString()))
42+
.ToList();
43+
44+
if (imagesForDeletion.Count == 0)
45+
return;
46+
47+
foreach (var image in imagesForDeletion)
48+
unitOfWork.Products.DeleteImage(image);
4649

47-
logger.LogInformation(
48-
"Image cleanup completed. Successfully deleted: {Deleted}, Failed: {Failed}",
49-
deletedCount,
50-
failedCount
51-
);
50+
await unitOfWork.SaveChangesAsync();
5251
}
5352
}

src/Riber.Infrastructure/Persistence/Repositories/ProductRepository.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ public async Task<IReadOnlyList<Image>> GetUnusedImagesAsync(CancellationToken c
4343
.AsNoTracking()
4444
.Where(new ImagesReadyForCleanupSpecification())
4545
.ToListAsync(cancellationToken);
46+
47+
public void DeleteImage(Image image)
48+
=> _images.Remove(image);
4649
}

src/Riber.Infrastructure/Services/AWS/AmazonS3Service.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ public AmazonS3Service(
2525
?? throw new ArgumentNullException(nameof(configuration), "Configuration cannot be null");
2626

2727
if (string.IsNullOrWhiteSpace(_bucketName))
28-
throw new InvalidOperationException(
29-
"AWS S3 bucket name is not configured. Please set 'AWS:S3:BucketImagesName' in configuration.");
28+
throw new InvalidOperationException("AWS S3 bucket name is not configured. Please set 'AWS:S3:BucketImagesName' in configuration.");
3029
}
3130

3231
public async Task UploadAsync(Stream stream, string fileName, string contentType)
@@ -76,7 +75,7 @@ public async Task<Stream> GetImageStreamAsync(string fileName)
7675
var request = new GetObjectRequest { BucketName = _bucketName, Key = fileName };
7776
var response = await _amazonS3.GetObjectAsync(request);
7877

79-
_logger.LogDebug("Successfully retrieved image {FileName} from S3 bucket {BucketName}",
78+
_logger.LogDebug("Successfully retrieved image {FileName} from S3 bucket {BucketName}",
8079
fileName, _bucketName);
8180

8281
return response.ResponseStream;
@@ -103,15 +102,28 @@ public async Task<Stream> GetImageStreamAsync(string fileName)
103102
}
104103
}
105104

106-
public async Task DeleteAsync(string fileName)
105+
public async Task<IEnumerable<string>> DeleteAllAsync(List<string> fileKeys)
107106
{
108107
try
109108
{
110-
var request = new DeleteObjectRequest { BucketName = _bucketName, Key = fileName };
109+
var request = new DeleteObjectsRequest
110+
{
111+
BucketName = _bucketName,
112+
Objects = [.. fileKeys.Select(x => new KeyVersion { Key = x })]
113+
};
111114

112-
await _amazonS3.DeleteObjectAsync(request);
113-
_logger.LogDebug("Successfully deleted image {FileName} from S3 bucket {BucketName}",
114-
fileName, _bucketName);
115+
var response = await _amazonS3.DeleteObjectsAsync(request);
116+
List<string> deletedKeys = [.. response.DeletedObjects.Select(x => x.Key)];
117+
var failedCount = response.DeleteErrors.Count;
118+
119+
_logger.LogInformation(
120+
"Deleted {SuccessCount} image(s), failed to delete {FailedCount} image(s) from S3 bucket {BucketName}",
121+
deletedKeys.Count,
122+
failedCount,
123+
_bucketName
124+
);
125+
126+
return deletedKeys;
115127
}
116128
catch (Exception ex)
117129
{

src/Riber.Infrastructure/Services/Local/LocalImageStorageService.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,20 @@ public Task<Stream> GetImageStreamAsync(string fileName)
5353
);
5454
}
5555

56-
public Task DeleteAsync(string fileName)
56+
public Task<IEnumerable<string>> DeleteAllAsync(List<string> fileKeys)
5757
{
58-
var imagePath = Path.Combine(_storagePath, fileName);
59-
if (!File.Exists(imagePath))
60-
throw new NotFoundException(StorageErrors.ImageNotFound);
58+
List<string> deletedKeys = [];
59+
foreach (var fileKey in fileKeys)
60+
{
61+
var imagePath = Path.Combine(_storagePath, fileKey);
62+
if (!File.Exists(imagePath))
63+
continue;
6164

62-
File.Delete(imagePath);
63-
return Task.CompletedTask;
65+
File.Delete(imagePath);
66+
deletedKeys.Add(fileKey);
67+
}
68+
69+
_logger.LogInformation("Deleted {Count} file(s)", deletedKeys.Count);
70+
return Task.FromResult(deletedKeys.AsEnumerable());
6471
}
6572
}

0 commit comments

Comments
 (0)