Update delete workflows

This commit is contained in:
2026-05-14 18:24:20 +02:00
parent 7215eebb5a
commit a4128d0953
30 changed files with 1098 additions and 293 deletions
@@ -1,7 +1,9 @@
using MassTransit;
using Amazon.S3;
using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments;
using System.Net;
namespace Musify.Infrastructure.MassTransit.Activities.Files
{
@@ -22,6 +24,16 @@ namespace Musify.Infrastructure.MassTransit.Activities.Files
executeContext.CancellationToken);
return executeContext.Completed();
}
catch (AmazonS3Exception exception) when (
exception.StatusCode == HttpStatusCode.NotFound
|| string.Equals(exception.ErrorCode, "NoSuchKey", StringComparison.OrdinalIgnoreCase))
{
logger.LogInformation(
"Key {Key} not found in bucket {Bucket}, skipping",
executeContext.Arguments.Key,
executeContext.Arguments.Bucket);
return executeContext.Completed();
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to remove {Key} from bucket {Bucket}",
@@ -0,0 +1,35 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure;
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
{
internal class DeletePlayListFromDbActivity(
IDatabase database,
ILogger<DeletePlayListFromDbActivity> logger)
: IExecuteActivity<DeletePlayListFromDbArguments>
{
public const string ExecuteEndpointName = "delete-playList-from-db";
public async Task<ExecutionResult> Execute(ExecuteContext<DeletePlayListFromDbArguments> executeContext)
{
var playList = await database.PlayLists
.SingleOrDefaultAsync(p => p.Id == executeContext.Arguments.PlayListId, executeContext.CancellationToken);
if (playList is null)
{
logger.LogInformation(
"PlayList {PlayListId} not found while deleting from DB, skipping",
executeContext.Arguments.PlayListId);
return executeContext.Completed();
}
database.PlayLists.Remove(playList);
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Deleted playlist {PlayListId} from DB", playList.Id);
return executeContext.Completed();
}
}
}
@@ -0,0 +1,5 @@
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
{
internal record DeletePlayListFromDbArguments(
Guid PlayListId);
}
@@ -0,0 +1,36 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Domain.Entities;
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
{
internal class MarkPlayListAsRemovingActivity(
IDatabase database,
ILogger<MarkPlayListAsRemovingActivity> logger)
: IExecuteActivity<MarkPlayListAsRemovingArguments>
{
public const string ExecuteEndpointName = "mark-playList-as-removing";
public async Task<ExecutionResult> Execute(ExecuteContext<MarkPlayListAsRemovingArguments> executeContext)
{
var playList = await database.PlayLists
.SingleOrDefaultAsync(p => p.Id == executeContext.Arguments.PlayListId, executeContext.CancellationToken);
if (playList is null)
{
logger.LogInformation(
"PlayList {PlayListId} not found while marking as removing, skipping",
executeContext.Arguments.PlayListId);
return executeContext.Completed();
}
playList.LifeCycleStatus = LifeCycleStatus.Removing;
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Marked playlist {PlayListId} as removing", playList.Id);
return executeContext.Completed();
}
}
}
@@ -0,0 +1,5 @@
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
{
internal record MarkPlayListAsRemovingArguments(
Guid PlayListId);
}
@@ -1,115 +0,0 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Configuration;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Domain.Entities;
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal class DeleteTrackActivity(
IDatabase database,
IStorageService storageService,
TrackConfiguration trackConfiguration,
ApplicationStorageConfiguration storageConfiguration,
ILogger<DeleteTrackActivity> logger)
: IExecuteActivity<DeleteTrackArguments>
{
public const string ExecuteEndpointName = "delete-track";
public async Task<ExecutionResult> Execute(ExecuteContext<DeleteTrackArguments> executeContext)
{
Track track;
try
{
var getTrack = await database.Tracks.SingleOrDefaultAsync(t => t.Id == executeContext.Arguments.TrackId, executeContext.CancellationToken);
track = getTrack ?? throw new InvalidOperationException($"Track with id {executeContext.Arguments.TrackId} not found");
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to retrieve track {TrackId} for deletion", executeContext.Arguments.TrackId);
throw;
}
try
{
await RemovePicturesAsync(track, executeContext.Arguments.UserId, executeContext.CancellationToken);
track.PicturesProcessingStatus = ProcessingStatus.Completed;
await database.SaveChangesAsync(executeContext.CancellationToken);
await RemoveAudiosAsync(track, executeContext.Arguments.UserId, executeContext.CancellationToken);
track.AudioTranscodeProcessingStatus = ProcessingStatus.Completed;
await database.SaveChangesAsync(executeContext.CancellationToken);
database.Tracks.Remove(track);
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Deleted track {TrackId}", track.Id);
return executeContext.Completed();
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to delete track {TrackId}", executeContext.Arguments.TrackId);
try
{
track.PicturesProcessingStatus = ProcessingStatus.Failed;
track.AudioTranscodeProcessingStatus = ProcessingStatus.Failed;
await database.SaveChangesAsync(executeContext.CancellationToken);
}
catch (Exception dbException)
{
logger.LogError(dbException, "Failed to update processing statuses to Failed for track {TrackId}", executeContext.Arguments.TrackId);
}
throw;
}
}
async Task RemovePicturesAsync(Track track, Guid userId, CancellationToken cancellationToken)
{
await RemoveFileAsync(trackConfiguration.Routes.BuildOriginalPicturePath(userId, track.OriginalPictureName), cancellationToken);
if (!track.IsPicturesProcessed)
{
logger.LogInformation(
"Track {TrackId} pictures are not fully processed, skipping removal of small, medium, and large pictures",
track.Id);
return;
}
await RemoveFileAsync(trackConfiguration.Routes.BuildSmallPicturePath(track.SmallPictureName), cancellationToken);
await RemoveFileAsync(trackConfiguration.Routes.BuildMediumPicturePath(track.MediumPictureName), cancellationToken);
await RemoveFileAsync(trackConfiguration.Routes.BuildLargePicturePath(track.LargePictureName), cancellationToken);
}
async Task RemoveAudiosAsync(Track track, Guid userId, CancellationToken cancellationToken)
{
await RemoveFileAsync(trackConfiguration.Routes.BuildOriginalAudioPath(userId, track.OriginalAudioName), cancellationToken);
if (!track.IsAudioProcessed)
{
logger.LogInformation(
"Track {TrackId} audio is not fully processed, skipping removal of processed audio",
track.Id);
return;
}
await RemoveFileAsync(trackConfiguration.Routes.BuildProcessedAudioPath(track.AudioFolderName), cancellationToken);
}
async Task RemoveFileAsync(string key, CancellationToken cancellationToken)
{
try
{
logger.LogDebug("Removing file {Key} from bucket {Bucket}", key, storageConfiguration.Bucket);
await storageService.RemoveFileAsync(storageConfiguration.Bucket, key, cancellationToken);
}
catch (Exception exception)
{
logger.LogError(exception, "Failed to remove file {Key} from bucket {Bucket}", key, storageConfiguration.Bucket);
throw;
}
}
}
}
@@ -1,6 +0,0 @@
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal record DeleteTrackArguments(
Guid TrackId,
Guid UserId);
}
@@ -0,0 +1,35 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure;
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal class DeleteTrackFromDbActivity(
IDatabase database,
ILogger<DeleteTrackFromDbActivity> logger)
: IExecuteActivity<DeleteTrackFromDbArguments>
{
public const string ExecuteEndpointName = "delete-track-from-db";
public async Task<ExecutionResult> Execute(ExecuteContext<DeleteTrackFromDbArguments> executeContext)
{
var track = await database.Tracks
.SingleOrDefaultAsync(t => t.Id == executeContext.Arguments.TrackId, executeContext.CancellationToken);
if (track is null)
{
logger.LogInformation(
"Track {TrackId} not found while deleting from DB, skipping",
executeContext.Arguments.TrackId);
return executeContext.Completed();
}
database.Tracks.Remove(track);
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Deleted track {TrackId} from DB", track.Id);
return executeContext.Completed();
}
}
}
@@ -0,0 +1,5 @@
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal record DeleteTrackFromDbArguments(
Guid TrackId);
}
@@ -0,0 +1,36 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Domain.Entities;
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal class MarkTrackAsRemovingActivity(
IDatabase database,
ILogger<MarkTrackAsRemovingActivity> logger)
: IExecuteActivity<MarkTrackAsRemovingArguments>
{
public const string ExecuteEndpointName = "mark-track-as-removing";
public async Task<ExecutionResult> Execute(ExecuteContext<MarkTrackAsRemovingArguments> executeContext)
{
var track = await database.Tracks
.SingleOrDefaultAsync(t => t.Id == executeContext.Arguments.TrackId, executeContext.CancellationToken);
if (track is null)
{
logger.LogInformation(
"Track {TrackId} not found while marking as removing, skipping",
executeContext.Arguments.TrackId);
return executeContext.Completed();
}
track.LifeCycleStatus = LifeCycleStatus.Removing;
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Marked track {TrackId} as removing", track.Id);
return executeContext.Completed();
}
}
}
@@ -0,0 +1,5 @@
namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{
internal record MarkTrackAsRemovingArguments(
Guid TrackId);
}