Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Backend/MyMusicLibrary.API/Controllers/MusicController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using MyMusicLibrary.Application.UseCases.Music.GetById;
using MyMusicLibrary.Application.UseCases.Music.Register;
using MyMusicLibrary.Application.UseCases.Music.Search;
using MyMusicLibrary.Application.UseCases.Music.Update;
using MyMusicLibrary.Application.UseCases.Music.Upload;
using MyMusicLibrary.Communication.Request;
using MyMusicLibrary.Communication.Responses;
Expand Down Expand Up @@ -105,4 +106,14 @@ public async Task<IActionResult> Upload([FromServices] IUploadMusicUseCase useCa
return BadRequest(ex.Message);
}
}

[HttpPut("update")]
[ProducesResponseType(typeof(ResponseProfileMusicJson), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Update([FromServices] IUpdateMusicUseCase useCase, [FromBody] RequestUpdateMusic request)
{
var result = await useCase.Execute(request);
return Ok(result);
}
}
1 change: 1 addition & 0 deletions src/Backend/MyMusicLibrary.API/MyMusicLibrary.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<InvariantGlobalization>false</InvariantGlobalization>
<WarningsAsErrors>true</WarningsAsErrors>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using MyMusicLibrary.Application.UseCases.Music.GetById;
using MyMusicLibrary.Application.UseCases.Music.Register;
using MyMusicLibrary.Application.UseCases.Music.Search;
using MyMusicLibrary.Application.UseCases.Music.Update;
using MyMusicLibrary.Application.UseCases.Music.Upload;
using MyMusicLibrary.Application.UseCases.Playlist.AddMusicToPlaylist;
using MyMusicLibrary.Application.UseCases.Playlist.Create;
Expand Down Expand Up @@ -78,5 +79,6 @@ private static void AddUseCases(IServiceCollection services)
services.AddScoped<IUnfavoriteUseCase, UnfavoriteUseCase>();
services.AddScoped<IUploadMusicUseCase, UploadMusicUseCase>();
services.AddScoped<IUseRefreshTokenUseCase, UseRefreshTokenUseCase>();
services.AddScoped<IUpdateMusicUseCase, UpdateMusicUseCase>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using MyMusicLibrary.Communication.Request;
using MyMusicLibrary.Communication.Responses;

namespace MyMusicLibrary.Application.UseCases.Music.Update;
public interface IUpdateMusicUseCase
{
Task<ResponseProfileMusicJson> Execute(RequestUpdateMusic request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using MyMusicLibrary.Communication.Request;
using MyMusicLibrary.Communication.Responses;
using MyMusicLibrary.Domain.Repositories.Music;
using MyMusicLibrary.Domain.Repositories.UnitOfWork;
using MyMusicLibrary.Domain.Services.LoggedUser;
using MyMusicLibrary.Domain.Services.Storage.Aws;
using MyMusicLibrary.Exceptions;
using MyMusicLibrary.Exceptions.ExceptionsBase;

namespace MyMusicLibrary.Application.UseCases.Music.Update;
public class UpdateMusicUseCase : IUpdateMusicUseCase
{
private readonly IMusicWriteOnlyRepository _musicWriteOnlyRepository;
private readonly ILoggedUser _loggedUser;
private readonly IMusicReadOnlyRepository _musicReadOnlyRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IS3Service _s3Service;

public UpdateMusicUseCase(IMusicWriteOnlyRepository musicWriteOnlyRepository,
ILoggedUser loggedUser,
IMusicReadOnlyRepository musicReadOnlyRepository,
IUnitOfWork unitOfWork,
IS3Service s3Service)
{
_musicWriteOnlyRepository = musicWriteOnlyRepository;
_loggedUser = loggedUser;
_musicReadOnlyRepository = musicReadOnlyRepository;
_unitOfWork = unitOfWork;
_s3Service = s3Service;
}

public async Task<ResponseProfileMusicJson> Execute(RequestUpdateMusic request)
{
var user = await _loggedUser.User();

var music = await _musicReadOnlyRepository.GetById(user, request.MusicId);
if (music is null)
throw new NotFoundException(ResourceMessagesException.MUSIC_EMPTY);

var oldKey = music.MusicKey!;
var extension = Path.GetExtension(oldKey);
var folder = Path.GetDirectoryName(oldKey)?.Replace("\\", "/");
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for constructing S3 keys by extracting folder paths and extensions could be fragile. S3 keys are not file system paths and don't have backslashes. The Path.GetDirectoryName method is designed for file system paths and may not handle S3 keys correctly in all cases. Consider using string manipulation with forward slashes directly or creating a dedicated method to parse S3 keys.

Suggested change
var folder = Path.GetDirectoryName(oldKey)?.Replace("\\", "/");
var lastSlashIndex = oldKey.LastIndexOf('/');
var folder = lastSlashIndex >= 0 ? oldKey.Substring(0, lastSlashIndex) : string.Empty;

Copilot uses AI. Check for mistakes.
var newKey = string.IsNullOrEmpty(folder)
? $"{request.Name}{extension}"
: $"{folder}/{request.Name}{extension}";

await _s3Service.RenameFileAsync(oldKey, newKey);

music.Name = request.Name;
music.MusicKey = newKey;

await _musicWriteOnlyRepository.Update(user, music);
await _unitOfWork.Commit();

Comment on lines +47 to +54
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the S3 rename operation succeeds but the database update fails, the S3 file will be renamed while the database still references the old key, causing data inconsistency. Consider implementing a rollback mechanism or reordering operations to update the database first, then rename the S3 file, so failures leave the system in a consistent state.

Suggested change
await _s3Service.RenameFileAsync(oldKey, newKey);
music.Name = request.Name;
music.MusicKey = newKey;
await _musicWriteOnlyRepository.Update(user, music);
await _unitOfWork.Commit();
music.Name = request.Name;
music.MusicKey = newKey;
await _musicWriteOnlyRepository.Update(user, music);
await _unitOfWork.Commit();
await _s3Service.RenameFileAsync(oldKey, newKey);

Copilot uses AI. Check for mistakes.
return new ResponseProfileMusicJson()
{
Name = music.Name
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ public interface IMusicWriteOnlyRepository
Task Delete(long musicId);
Task AddMusicFavorite(Entities.UserFavoritesMusic musicFavorite);
Task UnfavoriteMusic(long musicId);
Task Update(Entities.User user, Entities.Music music);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public interface IS3Service
Task<S3FilesDto> UploadFileAsync(IFormFile file);
Task DeleteFile(string key);
Task<S3UrlDto> GetFileUrl(string key);
Task RenameFileAsync(string oldKey, string newKey);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,16 @@ public async Task UnfavoriteMusic(long musicId)

_dbContext.UserFavoritesMusic.Remove(music!);
}

public async Task Update(Domain.Entities.User user, Domain.Entities.Music music)
{
var musicUpdate = await _dbContext.Music
.Where(m => m.Id == music.Id && m.UserId == user.Id)
.FirstOrDefaultAsync();

if (musicUpdate is null)
return;

Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Update method retrieves musicUpdate from the database but never applies the changes from the music parameter to it. The properties like Name and MusicKey should be copied from music to musicUpdate before calling _dbContext.Music.Update(musicUpdate).

Suggested change
// Copy updatable properties from music to musicUpdate
musicUpdate.Name = music.Name;
musicUpdate.MusicKey = music.MusicKey;
// Add other properties to update as needed

Copilot uses AI. Check for mistakes.
_dbContext.Music.Update(musicUpdate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,28 @@ public async Task<S3UrlDto> GetFileUrl(string key)

return await Task.FromResult(new S3UrlDto(url: url));
}

public async Task RenameFileAsync(string oldKey, string newKey)
{
// Copia o arquivo existente para o novo nome
var copyRequest = new CopyObjectRequest
{
SourceBucket = bucketName,
SourceKey = oldKey,
DestinationBucket = bucketName,
DestinationKey = newKey
};

await _s3Client.CopyObjectAsync(copyRequest);

// Remove o arquivo antigo
var deleteRequest = new DeleteObjectRequest
{
BucketName = bucketName,
Key = oldKey
};

await _s3Client.DeleteObjectAsync(deleteRequest);
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RenameFileAsync method has no error handling. If the copy operation succeeds but the delete operation fails, the old file will remain in S3, causing duplicate files and potential storage cost issues. Consider wrapping the delete operation in a try-catch or validating that the copy succeeded before attempting deletion.

Suggested change
await _s3Client.DeleteObjectAsync(deleteRequest);
try
{
await _s3Client.DeleteObjectAsync(deleteRequest);
}
catch (Exception ex)
{
// Log the error. Replace with your logging framework if available.
Console.WriteLine($"Failed to delete old S3 object '{oldKey}': {ex.Message}");
// Optionally, rethrow or handle as needed.
}

Copilot uses AI. Check for mistakes.
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MyMusicLibrary.Communication.Request;
public class RequestUpdateMusic
{
public long MusicId { get; set; }
public string Name { get; set; } = string.Empty;
}