From f6ac45cb7901d83a8b209f8ee7c04479f8cbe616 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Thu, 14 May 2026 01:00:44 +0200 Subject: [PATCH] Safe code and changes --- .../Handlers/CreatePlayListCommandHandler.cs | 21 +++ .../Handlers/DeletePlayListCommandHandler.cs | 76 ++++++-- .../GetPlayListsByUserIdQueryHandler.cs | 6 +- .../Handlers/UpdatePlayListCommandHandler.cs | 120 +++++++----- .../Queries/GetPlayListsByUserIdQuery.cs | 2 +- .../Handler/CreateTrackCommandHandler.cs | 66 ++++++- .../Handler/DeleteTrackCommandHandler.cs | 46 ++++- .../Handler/GetTracksByUserIdQueryHandler.cs | 6 +- .../Tracks/Handler/GetTracksQueryHandler.cs | 11 +- .../Tracks/Queries/GetTracksByUserIdQuery.cs | 2 +- .../Handlers/CreateUserCommandHandler.cs | 13 +- .../Users/Handlers/GetUsersQueryHandler.cs | 5 +- Application/Users/Queries/GetUsersQuery.cs | 2 +- .../Files/UploadFileToBucketActivity.cs | 1 - .../Pictures/ResizePictureActivity.cs | 1 - .../Pictures/UpdateTrackPictureActivity.cs | 1 - .../Consumers/RemoveFileConsumer.cs | 40 ++++ ...assTransitDependencyInjection.Endpoints.cs | 50 +++++ .../MassTransitDependencyInjection.Options.cs | 24 +++ ...TransitDependencyInjection.Registration.cs | 144 ++++++++++++++ .../MassTransitDependencyInjection.cs | 178 ++---------------- .../MassTransitEndpointNames.cs | 9 + .../MassTransit/Logs/UpdateTrackPictureLog.cs | 6 +- .../OpenTelemetryDependencyInjection.cs | 1 - .../20260328141805_FirstMigration.cs | 3 +- 25 files changed, 568 insertions(+), 266 deletions(-) create mode 100644 Infrastructure/MassTransit/Consumers/RemoveFileConsumer.cs create mode 100644 Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Endpoints.cs create mode 100644 Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Options.cs create mode 100644 Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs create mode 100644 Infrastructure/MassTransit/DependencyInjection/MassTransitEndpointNames.cs diff --git a/Application/PlayLists/Handlers/CreatePlayListCommandHandler.cs b/Application/PlayLists/Handlers/CreatePlayListCommandHandler.cs index 0bdb882..543cb4d 100644 --- a/Application/PlayLists/Handlers/CreatePlayListCommandHandler.cs +++ b/Application/PlayLists/Handlers/CreatePlayListCommandHandler.cs @@ -62,13 +62,31 @@ namespace Musify.Application.PlayLists.Handlers { var publishResult = await PublishPictureUpdateEventAsync(playList, cancellationToken); if (!publishResult.IsSuccess) + { + await TryRollbackOnPublishFailureAsync(playList, cancellationToken); return publishResult; + } } logger.LogInformation("Created playlist {PlayListId} for user {UserId}", playList.Id, request.UserId); return Result.Created(PlayListResponse.FromEntity(playList)); } + async Task TryRollbackOnPublishFailureAsync(PlayList playList, CancellationToken cancellationToken) + { + try + { + database.PlayLists.Remove(playList); + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to rollback playlist creation after publish failure for playlist {PlayListId}", playList.Id); + } + + await RollbackAllPicturesAsync(playList, cancellationToken); + } + async Task UploadPictureAsync(PlayList playList, IFileData picture, CancellationToken cancellationToken) { var pictureName = Guid.NewGuid() + picture.FileType; @@ -123,6 +141,9 @@ namespace Musify.Application.PlayLists.Handlers if (string.IsNullOrEmpty(playList.OriginalPictureName)) return; + if (playList.OriginalPictureName == playListConfiguration.Routes.PresetOriginalPicture) + return; + var originalPicturePath = playListConfiguration.Routes.BuildOriginalPicturePath(playList.OriginalPictureName); try diff --git a/Application/PlayLists/Handlers/DeletePlayListCommandHandler.cs b/Application/PlayLists/Handlers/DeletePlayListCommandHandler.cs index db0c188..900d44d 100644 --- a/Application/PlayLists/Handlers/DeletePlayListCommandHandler.cs +++ b/Application/PlayLists/Handlers/DeletePlayListCommandHandler.cs @@ -28,25 +28,73 @@ namespace Musify.Application.PlayLists.Handlers return Result.Unauthorized(); } - if (!string.IsNullOrWhiteSpace(playList.OriginalPictureName)) - await PublishRemoveFileEventAsync(playList.Id, - playListConfiguration.Routes.BuildOriginalPicturePath(playList.OriginalPictureName), cancellationToken); - if (playList.SmallPictureName != playListConfiguration.Routes.PresetSmallPicture) - await PublishRemoveFileEventAsync(playList.Id, - playListConfiguration.Routes.BuildSmallPicturePath(playList.SmallPictureName), cancellationToken); - if (playList.MediumPictureName != playListConfiguration.Routes.PresetMediumPicture) - await PublishRemoveFileEventAsync(playList.Id, - playListConfiguration.Routes.BuildMediumPicturePath(playList.MediumPictureName), cancellationToken); - if (playList.LargePictureName != playListConfiguration.Routes.PresetLargePicture) - await PublishRemoveFileEventAsync(playList.Id, - playListConfiguration.Routes.BuildLargePicturePath(playList.LargePictureName), cancellationToken); + var originalPictureName = playList.OriginalPictureName; + var smallPictureName = playList.SmallPictureName; + var mediumPictureName = playList.MediumPictureName; + var largePictureName = playList.LargePictureName; - database.PlayLists.Remove(playList); - await database.SaveChangesAsync(cancellationToken); + var originalPictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(playList.OriginalPictureName); + var smallPictureKey = playListConfiguration.Routes.BuildSmallPicturePath(playList.SmallPictureName); + var mediumPictureKey = playListConfiguration.Routes.BuildMediumPicturePath(playList.MediumPictureName); + var largePictureKey = playListConfiguration.Routes.BuildLargePicturePath(playList.LargePictureName); + + try + { + database.PlayLists.Remove(playList); + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to delete playlist {PlayListId}", request.PlayListId); + return Result.Error($"Failed to delete playlist {request.PlayListId}"); + } + + await PublishRemoveFileEventsBestEffortAsync( + playList.Id, + originalPictureName, + smallPictureName, + mediumPictureName, + largePictureName, + originalPictureKey, + smallPictureKey, + mediumPictureKey, + largePictureKey, + playListConfiguration.Routes, + cancellationToken); return Result.NoContent(); } + async Task PublishRemoveFileEventsBestEffortAsync( + Guid playListId, + string originalPictureName, + string smallPictureName, + string mediumPictureName, + string largePictureName, + string originalPictureKey, + string smallPictureKey, + string mediumPictureKey, + string largePictureKey, + PlayListRoutes playListRoutes, + CancellationToken cancellationToken) + { + if (originalPictureName != playListRoutes.PresetOriginalPicture) + await PublishRemoveFileEventIgnoringErrorsAsync(playListId, originalPictureKey, cancellationToken); + if (smallPictureName != playListRoutes.PresetSmallPicture) + await PublishRemoveFileEventIgnoringErrorsAsync(playListId, smallPictureKey, cancellationToken); + if (mediumPictureName != playListRoutes.PresetMediumPicture) + await PublishRemoveFileEventIgnoringErrorsAsync(playListId, mediumPictureKey, cancellationToken); + if (largePictureName != playListRoutes.PresetLargePicture) + await PublishRemoveFileEventIgnoringErrorsAsync(playListId, largePictureKey, cancellationToken); + } + + async Task PublishRemoveFileEventIgnoringErrorsAsync(Guid playListId, string key, CancellationToken cancellationToken) + { + var result = await PublishRemoveFileEventAsync(playListId, key, cancellationToken); + if (!result.IsSuccess) + logger.LogWarning("Remove file event publish failed for playlist {PlayListId} with key {Key}", playListId, key); + } + async Task PublishRemoveFileEventAsync(Guid playListId, string key, CancellationToken cancellationToken) { try diff --git a/Application/PlayLists/Handlers/GetPlayListsByUserIdQueryHandler.cs b/Application/PlayLists/Handlers/GetPlayListsByUserIdQueryHandler.cs index 3803418..6c7cedf 100644 --- a/Application/PlayLists/Handlers/GetPlayListsByUserIdQueryHandler.cs +++ b/Application/PlayLists/Handlers/GetPlayListsByUserIdQueryHandler.cs @@ -16,14 +16,16 @@ namespace Musify.Application.PlayLists.Handlers { var pageNumber = request.PageNumber < 1 ? 1 : request.PageNumber; var pageSize = request.PageSize < 1 ? 10 : request.PageSize; - var normalizedName = request.Name?.Trim().ToUpperInvariant() ?? string.Empty; var playListsQuery = database.PlayLists .AsNoTracking() .Where(p => p.UserId == request.UserId); - if (!string.IsNullOrWhiteSpace(normalizedName)) + if (request.Name is not null) + { + var normalizedName = request.Name.Trim().ToUpperInvariant(); playListsQuery = playListsQuery.Where(p => p.NormalizedName.Contains(normalizedName)); + } var totalCount = await playListsQuery.CountAsync(cancellationToken); diff --git a/Application/PlayLists/Handlers/UpdatePlayListCommandHandler.cs b/Application/PlayLists/Handlers/UpdatePlayListCommandHandler.cs index 2e643ec..2235d02 100644 --- a/Application/PlayLists/Handlers/UpdatePlayListCommandHandler.cs +++ b/Application/PlayLists/Handlers/UpdatePlayListCommandHandler.cs @@ -17,6 +17,9 @@ namespace Musify.Application.PlayLists.Handlers PlayListConfiguration playListConfiguration) : ICommandHandler> { + record UpdatePictureOutcome(Result Result, bool PictureUpdated, string? UploadedPictureKey); + record PreviousPlayListState(string Name, string NormalizedName, string Description, string OriginalPictureName); + public async ValueTask> Handle(UpdatePlayListCommand request, CancellationToken cancellationToken) { var playListEntity = await database.PlayLists.SingleOrDefaultAsync(pl => pl.Id == request.PlayListId, cancellationToken); @@ -32,11 +35,14 @@ namespace Musify.Application.PlayLists.Handlers return Result.Unauthorized(); } + var previousState = CapturePreviousState(playListEntity); + UpdateName(playListEntity, request.NewName); UpdateDescription(playListEntity, request.NewDescription); - var updatePictureResult = await UpdatePicture(playListEntity, request.NewPicture, cancellationToken); - if (!updatePictureResult.IsSuccess) - return updatePictureResult; + + var pictureOutcome = await UpdatePicture(playListEntity, request.NewPicture, cancellationToken); + if (!pictureOutcome.Result.IsSuccess) + return pictureOutcome.Result; try { @@ -45,20 +51,24 @@ namespace Musify.Application.PlayLists.Handlers } catch (Exception exception) { - await Rollback(playListEntity.OriginalPictureName, cancellationToken); + if (pictureOutcome.PictureUpdated && pictureOutcome.UploadedPictureKey is not null) + await Rollback(pictureOutcome.UploadedPictureKey, cancellationToken); logger.LogError(exception, "Failed to update playlist {PlayListId}", playListEntity.Id); return Result.Error($"Failed to update playlist {playListEntity.Id}"); } - try + if (pictureOutcome.PictureUpdated) { - await PublishPlayListPictureEvent(playListEntity, cancellationToken); - await RemoveOldPictures(playListEntity, cancellationToken); - } - catch (Exception exception) - { - logger.LogError(exception, "Failed to publish playlist picture update event or remove old pictures for playlist {PlayListId}", playListEntity.Id); - return Result.Error($"Failed to update playlist {playListEntity.Id}"); + var publishResult = await PublishPlayListPictureEvent(playListEntity, cancellationToken); + if (!publishResult.IsSuccess) + { + await TryRollbackAfterPublishFailureAsync( + playListEntity, + previousState, + pictureOutcome.UploadedPictureKey, + cancellationToken); + return Result.Error($"Failed to update playlist {playListEntity.Id}"); + } } logger.LogInformation("Updated playlist {PlayListId}", playListEntity.Id); @@ -67,26 +77,34 @@ namespace Musify.Application.PlayLists.Handlers void UpdateName(PlayList playList, string? newName) { - if (!string.IsNullOrWhiteSpace(newName)) - { - playList.Name = newName; - playList.NormalizedName = newName.Trim().ToUpperInvariant(); - } + if (newName is null) + return; + + playList.Name = newName; + playList.NormalizedName = newName.Trim().ToUpperInvariant(); } void UpdateDescription(PlayList playList, string? newDescription) { - if (!string.IsNullOrWhiteSpace(newDescription)) - playList.Description = newDescription; + if (newDescription is null) + return; + + playList.Description = newDescription; } - async Task UpdatePicture(PlayList playList, IFileData? newPicture, CancellationToken cancellationToken) + static PreviousPlayListState CapturePreviousState(PlayList playList) + => new(playList.Name, playList.NormalizedName, playList.Description, playList.OriginalPictureName); + + async Task UpdatePicture( + PlayList playList, + IFileData? newPicture, + CancellationToken cancellationToken) { if (newPicture is null) - return Result.Success(); + return new UpdatePictureOutcome(Result.Success(), false, null); var pictureName = Guid.NewGuid() + newPicture.FileType; - var pictureKey = Path.Combine(playListConfiguration.Routes.OriginalPicturesPath, pictureName); + var pictureKey = playListConfiguration.Routes.BuildOriginalPicturePath(pictureName); try { await storageHandler.UploadFileAsync( @@ -98,11 +116,11 @@ namespace Musify.Application.PlayLists.Handlers } catch { - return Result.Error("Failed to upload picture to storage"); + return new UpdatePictureOutcome(Result.Error("Failed to upload picture to storage"), false, null); } playList.OriginalPictureName = pictureName; - return Result.Success(); + return new UpdatePictureOutcome(Result.Success(), true, pictureKey); } async Task PublishPlayListPictureEvent(PlayList playList, CancellationToken cancellationToken) @@ -112,7 +130,7 @@ namespace Musify.Application.PlayLists.Handlers await eventBus.PublishAsync(new UpdatePlayListPictureEvent( playList.Id, storageConfiguration.Bucket, - Path.Combine(playListConfiguration.Routes.OriginalPicturesPath, playList.OriginalPictureName), + playListConfiguration.Routes.BuildOriginalPicturePath(playList.OriginalPictureName), new ImageSize( playListConfiguration.Routes.SmallPicturesPath, playListConfiguration.PicturesSizes.SmallPictureWidth, @@ -135,25 +153,6 @@ namespace Musify.Application.PlayLists.Handlers } } - async Task RemoveOldPictures(PlayList playList, CancellationToken cancellationToken) - { - if (playList.SmallPictureName != playListConfiguration.Routes.PresetSmallPicture) - await PublishRemoveFileEvent(Path.Combine( - playListConfiguration.Routes.SmallPicturesPath, - playList.SmallPictureName), - cancellationToken); - if (playList.MediumPictureName != playListConfiguration.Routes.PresetMediumPicture) - await PublishRemoveFileEvent(Path.Combine( - playListConfiguration.Routes.MediumPicturesPath, - playList.MediumPictureName), - cancellationToken); - if (playList.LargePictureName != playListConfiguration.Routes.PresetLargePicture) - await PublishRemoveFileEvent(Path.Combine( - playListConfiguration.Routes.LargePicturesPath, - playList.LargePictureName), - cancellationToken); - } - async Task Rollback(string pictureKey, CancellationToken cancellationToken) { await storageHandler.RemoveFileAsync( @@ -163,20 +162,37 @@ namespace Musify.Application.PlayLists.Handlers logger.LogInformation("Rolled back uploaded picture {PictureKey} from storage", pictureKey); } - async Task PublishRemoveFileEvent(string key, CancellationToken cancellationToken) + async Task TryRollbackAfterPublishFailureAsync( + PlayList playList, + PreviousPlayListState previousState, + string? uploadedPictureKey, + CancellationToken cancellationToken) { + playList.Name = previousState.Name; + playList.NormalizedName = previousState.NormalizedName; + playList.Description = previousState.Description; + playList.OriginalPictureName = previousState.OriginalPictureName; + try { - await eventBus.PublishAsync(new RemoveFileEvent( - storageConfiguration.Bucket, - key), cancellationToken); - return Result.NoContent(); + await database.SaveChangesAsync(cancellationToken); } catch (Exception exception) { - logger.LogError(exception, "Failed to publish remove file event for key {Key}", key); - return Result.Error($"Failed to publish remove file event for {key}"); + logger.LogError(exception, "Failed to rollback playlist state after publish failure for playlist {PlayListId}", playList.Id); + } + + if (uploadedPictureKey is null) + return; + + try + { + await Rollback(uploadedPictureKey, cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to rollback uploaded picture after publish failure for playlist {PlayListId}", playList.Id); } } } -} \ No newline at end of file +} diff --git a/Application/PlayLists/Queries/GetPlayListsByUserIdQuery.cs b/Application/PlayLists/Queries/GetPlayListsByUserIdQuery.cs index c745a09..8f5439e 100644 --- a/Application/PlayLists/Queries/GetPlayListsByUserIdQuery.cs +++ b/Application/PlayLists/Queries/GetPlayListsByUserIdQuery.cs @@ -7,7 +7,7 @@ namespace Musify.Application.PlayLists.Queries { public record GetPlayListsByUserIdQuery( Guid UserId, - string Name = "", + string? Name = null, int PageNumber = 1, int PageSize = 10) : IQuery> >; diff --git a/Application/Tracks/Handler/CreateTrackCommandHandler.cs b/Application/Tracks/Handler/CreateTrackCommandHandler.cs index 1144f76..e07eaa5 100644 --- a/Application/Tracks/Handler/CreateTrackCommandHandler.cs +++ b/Application/Tracks/Handler/CreateTrackCommandHandler.cs @@ -34,13 +34,35 @@ namespace Musify.Application.Tracks.Handler var fullPictureKey = trackConfiguration.Routes.BuildOriginalPicturePath(trackEntity.OriginalPictureName); var fullAudioKey = trackConfiguration.Routes.BuildOriginalAudioPath(trackEntity.OriginalAudioName); - await UploadFilesAsync(request, fullPictureKey, fullAudioKey, cancellationToken); + var uploadResult = await UploadFilesAsync(request, fullPictureKey, fullAudioKey, cancellationToken); + if (!uploadResult.IsSuccess) + return uploadResult; - await PublishUpdateEvent(trackEntity, cancellationToken); - await PublishTranscodeEvent(trackEntity, cancellationToken); + try + { + await database.Tracks.AddAsync(trackEntity, cancellationToken); + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to save track {Title} after uploading files", request.Title); + await RollbackFilesAsync(fullPictureKey, fullAudioKey, cancellationToken); + return Result.Error($"Failed to create track for {request.Title}"); + } - await database.Tracks.AddAsync(trackEntity, cancellationToken); - await database.SaveChangesAsync(cancellationToken); + var publishPictureResult = await PublishUpdateEvent(trackEntity, cancellationToken); + if (!publishPictureResult.IsSuccess) + { + await RollbackTrackAfterPublishFailureAsync(trackEntity, fullPictureKey, fullAudioKey, cancellationToken); + return Result.Error($"Failed to create track for {request.Title}"); + } + + var publishTranscodeResult = await PublishTranscodeEvent(trackEntity, cancellationToken); + if (!publishTranscodeResult.IsSuccess) + { + await RollbackTrackAfterPublishFailureAsync(trackEntity, fullPictureKey, fullAudioKey, cancellationToken); + return Result.Error($"Failed to create track for {request.Title}"); + } return Result.Created(TrackResponse.FromEntity(trackEntity)); } @@ -91,7 +113,7 @@ namespace Musify.Application.Tracks.Handler logger.LogInformation("Rolled back uploaded files {PictureKey}, {AudioKey}", pictureKey, audioKey); } - async Task PublishUpdateEvent(Track track, CancellationToken cancellationToken) + async Task PublishUpdateEvent(Track track, CancellationToken cancellationToken) { try { @@ -111,14 +133,16 @@ namespace Musify.Application.Tracks.Handler trackConfiguration.Routes.LargePicturesPath, trackConfiguration.PicturesSizes.LargePictureWidth, trackConfiguration.PicturesSizes.LargePictureHeight)), cancellationToken); + return Result.Success(); } catch (Exception exception) { logger.LogError(exception, "Failed to publish track picture update event for track {TrackId}", track.Id); + return Result.Error($"Failed to publish track picture update event for track {track.Id}"); } } - async Task PublishTranscodeEvent(Track track, CancellationToken cancellationToken) + async Task PublishTranscodeEvent(Track track, CancellationToken cancellationToken) { var destinationFolderAudio = trackConfiguration.Routes.BuildProcessedAudioPath(Guid.NewGuid().ToString()); @@ -130,10 +154,38 @@ namespace Musify.Application.Tracks.Handler trackConfiguration.Routes.BuildOriginalAudioPath(track.OriginalAudioName), storageConfiguration.Bucket, destinationFolderAudio), cancellationToken); + return Result.Success(); } catch (Exception exception) { logger.LogError(exception, "Failed to publish track audio update event for track {TrackId}", track.Id); + return Result.Error($"Failed to publish track audio update event for track {track.Id}"); + } + } + + async Task RollbackTrackAfterPublishFailureAsync( + Track track, + string pictureKey, + string audioKey, + CancellationToken cancellationToken) + { + try + { + database.Tracks.Remove(track); + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to rollback track {TrackId} persistence after publish failure", track.Id); + } + + try + { + await RollbackFilesAsync(pictureKey, audioKey, cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to rollback uploaded files after publish failure for track {TrackId}", track.Id); } } } diff --git a/Application/Tracks/Handler/DeleteTrackCommandHandler.cs b/Application/Tracks/Handler/DeleteTrackCommandHandler.cs index fff04b9..8b41fd8 100644 --- a/Application/Tracks/Handler/DeleteTrackCommandHandler.cs +++ b/Application/Tracks/Handler/DeleteTrackCommandHandler.cs @@ -24,7 +24,10 @@ namespace Musify.Application.Tracks.Handler return Result.NotFound(); } - if (track.Id != request.UserId) + var isOwner = await database.UserHasTracks + .AsNoTracking() + .AnyAsync(ut => ut.TrackId == request.TrackId && ut.UserId == request.UserId, cancellationToken); + if (!isOwner) { logger.LogWarning("User {UserId} unauthorized to delete track {TrackId}", request.UserId, request.TrackId); return Result.Unauthorized(); @@ -37,13 +40,46 @@ namespace Musify.Application.Tracks.Handler return Result.Conflict("Track is currently being processed and cannot be deleted"); } + var previousPicturesStatus = track.PicturesProcessingStatus; + var previousAudioStatus = track.AudioTranscodeProcessingStatus; + track.PicturesProcessingStatus = ProcessingStatus.Processing; track.AudioTranscodeProcessingStatus = ProcessingStatus.Processing; - await database.SaveChangesAsync(cancellationToken); - await eventBus.PublishAsync( - new DeleteTrackEvent(track.Id, request.UserId), - cancellationToken); + try + { + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to mark track {TrackId} as processing before deletion", request.TrackId); + return Result.Error($"Failed to delete track {request.TrackId}"); + } + + try + { + await eventBus.PublishAsync( + new DeleteTrackEvent(track.Id, request.UserId), + cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to publish delete track event for track {TrackId}", request.TrackId); + + track.PicturesProcessingStatus = previousPicturesStatus; + track.AudioTranscodeProcessingStatus = previousAudioStatus; + + try + { + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception rollbackException) + { + logger.LogError(rollbackException, "Failed to rollback track {TrackId} processing flags after publish failure", request.TrackId); + } + + return Result.Error($"Failed to delete track {request.TrackId}"); + } return Result.NoContent(); } diff --git a/Application/Tracks/Handler/GetTracksByUserIdQueryHandler.cs b/Application/Tracks/Handler/GetTracksByUserIdQueryHandler.cs index 3b107c6..c48426f 100644 --- a/Application/Tracks/Handler/GetTracksByUserIdQueryHandler.cs +++ b/Application/Tracks/Handler/GetTracksByUserIdQueryHandler.cs @@ -16,15 +16,17 @@ namespace Musify.Application.Tracks.Handler { var pageNumber = request.PageNumber < 1 ? 1 : request.PageNumber; var pageSize = request.PageSize < 1 ? 10 : request.PageSize; - var normalizedName = request.Name?.Trim().ToUpperInvariant() ?? string.Empty; var tracksQuery = database.UserHasTracks .AsNoTracking() .Include(ut => ut.Track) .Where(p => p.UserId == request.UserId); - if (!string.IsNullOrWhiteSpace(normalizedName)) + if (request.Name is not null) + { + var normalizedName = request.Name.Trim().ToUpperInvariant(); tracksQuery = tracksQuery.Where(t => t.Track.NormalizedTitle.Contains(normalizedName)); + } var totalCount = await tracksQuery.CountAsync(cancellationToken); diff --git a/Application/Tracks/Handler/GetTracksQueryHandler.cs b/Application/Tracks/Handler/GetTracksQueryHandler.cs index 8fc47f9..1f3528c 100644 --- a/Application/Tracks/Handler/GetTracksQueryHandler.cs +++ b/Application/Tracks/Handler/GetTracksQueryHandler.cs @@ -17,15 +17,16 @@ namespace Musify.Application.Tracks.Handler var pageNumber = request.PageNumber < 1 ? 1 : request.PageNumber; var pageSize = request.PageSize < 1 ? 10 : request.PageSize; - var totalCount = await database.PlayLists.CountAsync(cancellationToken); - var pagedPlayLists = await database.UserHasTracks + var tracksQuery = database.UserHasTracks .AsNoTracking() .OrderBy(t => t.Id) .Include(ut => ut.Track) - .Select(t => TrackResponse.FromEntity(t.Track)) - .ToPagedListAsync(pageNumber, pageSize, totalCount, cancellationToken); + .Select(t => TrackResponse.FromEntity(t.Track)); - return Result.Success(PaginatedResponse.FromPagedList(pagedPlayLists)); + var totalCount = await tracksQuery.CountAsync(cancellationToken); + var pagedTracks = await tracksQuery.ToPagedListAsync(pageNumber, pageSize, totalCount, cancellationToken); + + return Result.Success(PaginatedResponse.FromPagedList(pagedTracks)); } } } \ No newline at end of file diff --git a/Application/Tracks/Queries/GetTracksByUserIdQuery.cs b/Application/Tracks/Queries/GetTracksByUserIdQuery.cs index 240cbbf..f4eff24 100644 --- a/Application/Tracks/Queries/GetTracksByUserIdQuery.cs +++ b/Application/Tracks/Queries/GetTracksByUserIdQuery.cs @@ -7,7 +7,7 @@ namespace Musify.Application.Tracks.Queries { public record GetTracksByUserIdQuery( Guid UserId, - string Name = "", + string? Name = null, int PageNumber = 1, int PageSize = 10) : IQuery > >; diff --git a/Application/Users/Handlers/CreateUserCommandHandler.cs b/Application/Users/Handlers/CreateUserCommandHandler.cs index 9d9e097..2c6b11f 100644 --- a/Application/Users/Handlers/CreateUserCommandHandler.cs +++ b/Application/Users/Handlers/CreateUserCommandHandler.cs @@ -21,8 +21,17 @@ namespace Musify.Application.Users.Handlers } var newUser = CreateUserCommand.ToEntity(request); - await database.Users.AddAsync(newUser, cancellationToken); - await database.SaveChangesAsync(cancellationToken); + + try + { + await database.Users.AddAsync(newUser, cancellationToken); + await database.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to create user {UserId}", request.Id); + return Result.Error($"Failed to create user {request.Id}"); + } logger.LogInformation("Created user {UserId}", newUser.Id); return Result.Created(UserResponse.FromEntity(newUser)); diff --git a/Application/Users/Handlers/GetUsersQueryHandler.cs b/Application/Users/Handlers/GetUsersQueryHandler.cs index 3f807d8..56c31cb 100644 --- a/Application/Users/Handlers/GetUsersQueryHandler.cs +++ b/Application/Users/Handlers/GetUsersQueryHandler.cs @@ -6,7 +6,6 @@ using Musify.Application.Contracts.Infrastructure; using Musify.Application.Users.Queries; using Musify.Application.Users.Responses; using X.PagedList.EF; -using X.PagedList.Extensions; namespace Musify.Application.Users.Handlers { @@ -17,12 +16,12 @@ namespace Musify.Application.Users.Handlers { var pageNumber = request.PageNumber < 1 ? 1 : request.PageNumber; var pageSize = request.PageSize < 1 ? 10 : request.PageSize; - var first = (pageNumber - 1) * pageSize; var user = database.Users .AsNoTracking() .AsQueryable(); - if (!string.IsNullOrEmpty(request.UsernameSearch)) + + if (request.UsernameSearch is not null) { var normalizedUsername = request.UsernameSearch.Trim().ToUpperInvariant(); user = user.Where(u => u.NormalizedName.Contains(normalizedUsername)); diff --git a/Application/Users/Queries/GetUsersQuery.cs b/Application/Users/Queries/GetUsersQuery.cs index 62ba046..6f403aa 100644 --- a/Application/Users/Queries/GetUsersQuery.cs +++ b/Application/Users/Queries/GetUsersQuery.cs @@ -5,6 +5,6 @@ using Musify.Application.Users.Responses; namespace Musify.Application.Users.Queries { - public record GetUsersQuery(int PageNumber, int PageSize, string UsernameSearch) + public record GetUsersQuery(int PageNumber, int PageSize, string? UsernameSearch = null) : IQuery >>; } diff --git a/Infrastructure/MassTransit/Activities/Files/UploadFileToBucketActivity.cs b/Infrastructure/MassTransit/Activities/Files/UploadFileToBucketActivity.cs index 415d68c..0b675b2 100644 --- a/Infrastructure/MassTransit/Activities/Files/UploadFileToBucketActivity.cs +++ b/Infrastructure/MassTransit/Activities/Files/UploadFileToBucketActivity.cs @@ -1,6 +1,5 @@ using MassTransit; using Microsoft.Extensions.Logging; -using MimeMapping; using Musify.Application.Contracts.Infrastructure; using Musify.Infrastructure.MassTransit.Arguments; using Musify.Infrastructure.MassTransit.Logs; diff --git a/Infrastructure/MassTransit/Activities/Pictures/ResizePictureActivity.cs b/Infrastructure/MassTransit/Activities/Pictures/ResizePictureActivity.cs index e066806..941132b 100644 --- a/Infrastructure/MassTransit/Activities/Pictures/ResizePictureActivity.cs +++ b/Infrastructure/MassTransit/Activities/Pictures/ResizePictureActivity.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using Musify.Application.Contracts.Infrastructure; using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Logs; -using Musify.Infrastructure.MassTransit.RoutingSlip.Builders; namespace Musify.Infrastructure.MassTransit.Activities { diff --git a/Infrastructure/MassTransit/Activities/Pictures/UpdateTrackPictureActivity.cs b/Infrastructure/MassTransit/Activities/Pictures/UpdateTrackPictureActivity.cs index 44392a1..3c807b8 100644 --- a/Infrastructure/MassTransit/Activities/Pictures/UpdateTrackPictureActivity.cs +++ b/Infrastructure/MassTransit/Activities/Pictures/UpdateTrackPictureActivity.cs @@ -1,7 +1,6 @@ using MassTransit; using Microsoft.Extensions.Logging; using Musify.Application.Contracts.Infrastructure; -using Musify.Domain.Entities; using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Logs; diff --git a/Infrastructure/MassTransit/Consumers/RemoveFileConsumer.cs b/Infrastructure/MassTransit/Consumers/RemoveFileConsumer.cs new file mode 100644 index 0000000..38345ad --- /dev/null +++ b/Infrastructure/MassTransit/Consumers/RemoveFileConsumer.cs @@ -0,0 +1,40 @@ +using MassTransit; +using Microsoft.Extensions.Logging; +using Musify.Application.Contracts.Infrastructure; +using Musify.Application.Events; + +namespace Musify.Infrastructure.MassTransit.Consumers +{ + public class RemoveFileConsumer( + IStorageService storageService, + ILogger logger) + : IConsumer + { + public const string QueueName = "remove-file"; + + public async Task Consume(ConsumeContext consumeContext) + { + try + { + await storageService.RemoveFileAsync( + consumeContext.Message.Bucket, + consumeContext.Message.Key, + consumeContext.CancellationToken); + + logger.LogInformation( + "Removed file {Key} from bucket {Bucket}", + consumeContext.Message.Key, + consumeContext.Message.Bucket); + } + catch (Exception exception) + { + logger.LogError( + exception, + "Failed to remove file {Key} from bucket {Bucket}", + consumeContext.Message.Key, + consumeContext.Message.Bucket); + throw; + } + } + } +} diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Endpoints.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Endpoints.cs new file mode 100644 index 0000000..023ea42 --- /dev/null +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Endpoints.cs @@ -0,0 +1,50 @@ +using MassTransit; +using Musify.Infrastructure.MassTransit.Consumers; + +namespace Musify.Infrastructure.MassTransit +{ + public static partial class MassTransitDependencyInjection + { + static void ConfigureConsumerEndpoint( + IRabbitMqBusFactoryConfigurator busFactoryConfigurator, + IBusRegistrationContext busRegistrationContext, + string queueName) + where TConsumer : class, IConsumer + { + busFactoryConfigurator.ReceiveEndpoint(queueName, endpointConfigurator => + endpointConfigurator.ConfigureConsumer(busRegistrationContext)); + } + + static void ConfigureExecuteActivityEndpoint( + IRabbitMqBusFactoryConfigurator busFactoryConfigurator, + IBusRegistrationContext busRegistrationContext, + string endpointName) + where TActivity : class, IExecuteActivity + where TArguments : class + { + busFactoryConfigurator.ReceiveEndpoint(MassTransitEndpointNames.ExecuteQueue(endpointName), endpointConfigurator => + { + endpointConfigurator.ExecuteActivityHost(busRegistrationContext); + }); + } + + static void ConfigureActivityEndpoint( + IRabbitMqBusFactoryConfigurator busFactoryConfigurator, + IBusRegistrationContext busRegistrationContext, + string endpointName) + where TActivity : class, IActivity + where TArguments : class + where TLog : class + { + busFactoryConfigurator.ReceiveEndpoint(MassTransitEndpointNames.ExecuteQueue(endpointName), endpointConfigurator => + endpointConfigurator.ExecuteActivityHost( + EndpointHelper.BuildCompensateActivityUri(endpointName), + busRegistrationContext) + ); + + busFactoryConfigurator.ReceiveEndpoint(MassTransitEndpointNames.CompensateQueue(endpointName), endpointConfigurator => + endpointConfigurator.CompensateActivityHost(busRegistrationContext) + ); + } + } +} diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Options.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Options.cs new file mode 100644 index 0000000..3255d0a --- /dev/null +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Options.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Musify.Infrastructure.MassTransit +{ + public static partial class MassTransitDependencyInjection + { + static void RegisterValidatedOptions( + IServiceCollection services, + IConfiguration configuration, + string sectionName) + where TOptions : class + { + services + .AddOptionsWithValidateOnStart() + .Bind(configuration.GetRequiredSection(sectionName)) + .ValidateDataAnnotations(); + + services.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService>().Value); + } + } +} diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs new file mode 100644 index 0000000..f1620cd --- /dev/null +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.Registration.cs @@ -0,0 +1,144 @@ +using MassTransit; +using Microsoft.Extensions.DependencyInjection; +using Musify.Infrastructure.Configuration; +using Musify.Infrastructure.MassTransit.Activities; +using Musify.Infrastructure.MassTransit.Activities.Arguments; +using Musify.Infrastructure.MassTransit.Activities.Audio; +using Musify.Infrastructure.MassTransit.Activities.Files; +using Musify.Infrastructure.MassTransit.Activities.Logs; +using Musify.Infrastructure.MassTransit.Activities.Tracks; +using Musify.Infrastructure.MassTransit.Arguments; +using Musify.Infrastructure.MassTransit.Consumers; +using Musify.Infrastructure.MassTransit.Logs; +using Musify.Infrastructure.MassTransit.RoutingSlip.Builders; + +namespace Musify.Infrastructure.MassTransit +{ + public static partial class MassTransitDependencyInjection + { + static void RegisterRoutingSlipBuilders(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + + static void RegisterConsumersAndActivities(IBusRegistrationConfigurator options) + { + options.AddConsumer(); + options.AddConsumer(); + options.AddConsumer(); + options.AddConsumer(); + options.AddConsumer(); + options.AddConsumer(); + + options.AddExecuteActivity(); + options.AddExecuteActivity(); + options.AddExecuteActivity(); + options.AddExecuteActivity(); + + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + options.AddActivity(); + } + + static void ConfigureRabbitMqWorkerEndpoints( + IBusRegistrationContext busRegistrationContext, + IRabbitMqBusFactoryConfigurator busFactoryConfigurator) + { + ConfigureRabbitMqHost( + busFactoryConfigurator, + busRegistrationContext.GetRequiredService()); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdatePlayListPictureConsumer.QueueName); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdateTrackAudioConsumer.QueueName); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdateTrackPictureConsumer.QueueName); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + DeleteTrackConsumer.QueueName); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + RemoveFileConsumer.QueueName); + + ConfigureConsumerEndpoint( + busFactoryConfigurator, + busRegistrationContext, + RoutingSlipCleanUpConsumer.QueueName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + RemoveFileFromBucketActivity.ExecuteEndpointName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + GenerateAudioWorkflowPathsActivity.ExecuteEndpointName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + GeneratePictureWorkflowPathsActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + ResizePictureActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdatePlayListPictureActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdateTrackPictureActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + DownloadFileFromBucketActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + TranscodeDashAudioActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + TransferFilesToBucketActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdateTrackAudioActivity.ExecuteEndpointName); + + ConfigureActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UploadFileToBucketActivity.ExecuteEndpointName); + } + } +} diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.cs index 499e8b0..75f3132 100644 --- a/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.cs +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitDependencyInjection.cs @@ -1,33 +1,19 @@ using MassTransit; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Musify.Application.Contracts.Infrastructure; using Musify.Infrastructure.Configuration; -using Musify.Infrastructure.MassTransit.Activities; -using Musify.Infrastructure.MassTransit.Activities.Arguments; -using Musify.Infrastructure.MassTransit.Activities.Audio; -using Musify.Infrastructure.MassTransit.Activities.Files; -using Musify.Infrastructure.MassTransit.Activities.Logs; -using Musify.Infrastructure.MassTransit.Arguments; -using Musify.Infrastructure.MassTransit.Consumers; -using Musify.Infrastructure.MassTransit.Logs; -using Musify.Infrastructure.MassTransit.RoutingSlip.Builders; -using Musify.Infrastructure.MassTransit.Activities.Tracks; namespace Musify.Infrastructure.MassTransit { - public static class MassTransitDependencyInjection + public static partial class MassTransitDependencyInjection { public static IServiceCollection AddMassTransitClient(this IServiceCollection serviceDescriptors, IConfiguration configuration) { - serviceDescriptors - .AddOptionsWithValidateOnStart() - .Bind(configuration.GetRequiredSection(MassTransitConfiguration.SectionName)) - .ValidateDataAnnotations(); - - serviceDescriptors.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService>().Value); + RegisterValidatedOptions( + serviceDescriptors, + configuration, + MassTransitConfiguration.SectionName); serviceDescriptors.AddScoped(); serviceDescriptors.AddMassTransit(options => @@ -43,124 +29,24 @@ namespace Musify.Infrastructure.MassTransit public static IServiceCollection AddMassTransitConsumers(this IServiceCollection serviceDescriptors, IConfiguration configuration) { - serviceDescriptors - .AddOptionsWithValidateOnStart() - .Bind(configuration.GetRequiredSection(MassTransitConfiguration.SectionName)) - .ValidateDataAnnotations(); + RegisterValidatedOptions( + serviceDescriptors, + configuration, + MassTransitConfiguration.SectionName); - serviceDescriptors.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService>().Value); + RegisterValidatedOptions( + serviceDescriptors, + configuration, + WorkerConfiguration.SectionName); - serviceDescriptors - .AddOptionsWithValidateOnStart() - .Bind(configuration.GetRequiredSection(WorkerConfiguration.SectionName)) - .ValidateDataAnnotations(); - - serviceDescriptors.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService>().Value); - - serviceDescriptors.AddScoped(); - serviceDescriptors.AddScoped(); - serviceDescriptors.AddScoped(); + RegisterRoutingSlipBuilders(serviceDescriptors); serviceDescriptors.AddMassTransit(options => { - options.AddConsumer(); - options.AddConsumer(); - options.AddConsumer(); - options.AddConsumer(); - options.AddConsumer(); - - options.AddExecuteActivity(); - options.AddExecuteActivity(); - options.AddExecuteActivity(); - options.AddExecuteActivity(); - - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); - options.AddActivity(); + RegisterConsumersAndActivities(options); options.UsingRabbitMq((busRegistrationContext, busFactoryConfigurator) => - { - ConfigureRabbitMqHost( - busFactoryConfigurator, - busRegistrationContext.GetRequiredService()); - - busFactoryConfigurator.ReceiveEndpoint(UpdatePlayListPictureConsumer.QueueName, endpointConfigurator => - endpointConfigurator.ConfigureConsumer(busRegistrationContext)); - - busFactoryConfigurator.ReceiveEndpoint(UpdateTrackAudioConsumer.QueueName, endpointConfigurator => - endpointConfigurator.ConfigureConsumer(busRegistrationContext)); - - busFactoryConfigurator.ReceiveEndpoint(UpdateTrackPictureConsumer.QueueName, endpointConfigurator => - endpointConfigurator.ConfigureConsumer(busRegistrationContext)); - - busFactoryConfigurator.ReceiveEndpoint(DeleteTrackConsumer.QueueName, endpointConfigurator => - endpointConfigurator.ConfigureConsumer(busRegistrationContext)); - - busFactoryConfigurator.ReceiveEndpoint(RoutingSlipCleanUpConsumer.QueueName, endpointConfigurator => - endpointConfigurator.ConfigureConsumer(busRegistrationContext)); - - ConfigureExecuteActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - RemoveFileFromBucketActivity.ExecuteEndpointName); - - ConfigureExecuteActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - GenerateAudioWorkflowPathsActivity.ExecuteEndpointName); - - ConfigureExecuteActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - GeneratePictureWorkflowPathsActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - ResizePictureActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - UpdatePlayListPictureActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - UpdateTrackPictureActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - DownloadFileFromBucketActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - TranscodeDashAudioActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - TransferFilesToBucketActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - UpdateTrackAudioActivity.ExecuteEndpointName); - - ConfigureActivityEndpoint( - busFactoryConfigurator, - busRegistrationContext, - UploadFileToBucketActivity.ExecuteEndpointName); - } ); + ConfigureRabbitMqWorkerEndpoints(busRegistrationContext, busFactoryConfigurator)); } ); return serviceDescriptors; @@ -176,37 +62,5 @@ namespace Musify.Infrastructure.MassTransit options.Password(massTransitConfiguration.Password); } ); } - - static void ConfigureExecuteActivityEndpoint( - IRabbitMqBusFactoryConfigurator busFactoryConfigurator, - IBusRegistrationContext busRegistrationContext, - string endpointName) - where TActivity : class, IExecuteActivity - where TArguments : class - { - busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_execute", endpointConfigurator => - { - endpointConfigurator.ExecuteActivityHost(busRegistrationContext); - } ); - } - - static void ConfigureActivityEndpoint( - IRabbitMqBusFactoryConfigurator busFactoryConfigurator, - IBusRegistrationContext busRegistrationContext, - string endpointName) - where TActivity : class, IActivity - where TArguments : class - where TLog : class - { - busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_execute", endpointConfigurator => - endpointConfigurator.ExecuteActivityHost( - EndpointHelper.BuildCompensateActivityUri(endpointName), - busRegistrationContext) - ); - - busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_compensate", endpointConfigurator => - endpointConfigurator.CompensateActivityHost(busRegistrationContext) - ); - } } } \ No newline at end of file diff --git a/Infrastructure/MassTransit/DependencyInjection/MassTransitEndpointNames.cs b/Infrastructure/MassTransit/DependencyInjection/MassTransitEndpointNames.cs new file mode 100644 index 0000000..cdcdffc --- /dev/null +++ b/Infrastructure/MassTransit/DependencyInjection/MassTransitEndpointNames.cs @@ -0,0 +1,9 @@ +namespace Musify.Infrastructure.MassTransit +{ + internal static class MassTransitEndpointNames + { + internal static string ExecuteQueue(string endpointName) => $"{endpointName}_execute"; + + internal static string CompensateQueue(string endpointName) => $"{endpointName}_compensate"; + } +} diff --git a/Infrastructure/MassTransit/Logs/UpdateTrackPictureLog.cs b/Infrastructure/MassTransit/Logs/UpdateTrackPictureLog.cs index a760eb7..c6e7387 100644 --- a/Infrastructure/MassTransit/Logs/UpdateTrackPictureLog.cs +++ b/Infrastructure/MassTransit/Logs/UpdateTrackPictureLog.cs @@ -3,7 +3,7 @@ namespace Musify.Infrastructure.MassTransit.Activities.Logs public record UpdateTrackPictureLog( Guid TrackId, string PreviousOriginalPictureKey, - string PreviousSmallPictureKey, - string PreviousMediumPictureKey, - string PreviousLargePictureKey); + string? PreviousSmallPictureKey, + string? PreviousMediumPictureKey, + string? PreviousLargePictureKey); } diff --git a/Infrastructure/Observability/OpenTelemetryDependencyInjection.cs b/Infrastructure/Observability/OpenTelemetryDependencyInjection.cs index 83c5c1f..0ac4f0c 100644 --- a/Infrastructure/Observability/OpenTelemetryDependencyInjection.cs +++ b/Infrastructure/Observability/OpenTelemetryDependencyInjection.cs @@ -6,7 +6,6 @@ using OpenTelemetry.Exporter; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; -using SixLabors.ImageSharp; namespace Musify.Infrastructure.Observability { diff --git a/Infrastructure/Persistence/Migrations/20260328141805_FirstMigration.cs b/Infrastructure/Persistence/Migrations/20260328141805_FirstMigration.cs index d184dd8..3c04607 100644 --- a/Infrastructure/Persistence/Migrations/20260328141805_FirstMigration.cs +++ b/Infrastructure/Persistence/Migrations/20260328141805_FirstMigration.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable