diff --git a/Edge.MyMusic/src/Edge.MyMusic.Tests/MusicServiceTests.cs b/Edge.MyMusic/src/Edge.MyMusic.Tests/MusicServiceTests.cs index cde1deb..9879ec1 100644 --- a/Edge.MyMusic/src/Edge.MyMusic.Tests/MusicServiceTests.cs +++ b/Edge.MyMusic/src/Edge.MyMusic.Tests/MusicServiceTests.cs @@ -229,7 +229,7 @@ public async Task CanUpdateFavoriteSongAsync() } - internal void AssertEqual(MusicDocument expected, SongResponse actual) + private static void AssertEqual(MusicDocument expected, SongResponse actual) { Assert.Equal(expected.Id, actual.Id); Assert.Equal(expected.Album, actual.Album); diff --git a/Edge.MyMusic/src/Edge.MyMusic.Tests/StartupProcessorTests.cs b/Edge.MyMusic/src/Edge.MyMusic.Tests/StartupProcessorTests.cs new file mode 100644 index 0000000..f3b8810 --- /dev/null +++ b/Edge.MyMusic/src/Edge.MyMusic.Tests/StartupProcessorTests.cs @@ -0,0 +1,61 @@ +using Edge.MyMusic.Processors; +using Edge.MyMusic.Providers; +using Edge.MyMusic.Repositories; +using Edge.MyMusic.Settings; +using Microsoft.Extensions.Logging; + +namespace Edge.MyMusic.Tests; + +public class StartupProcessorTests +{ + private readonly Mock> _logger; + private readonly Mock _repo; + private readonly Mock _audioProvider; + private readonly Mock _setting; + + private readonly StartupProcessor _processor; + + public StartupProcessorTests() + { + _logger = new Mock>(); + _repo = new Mock(); + _audioProvider = new Mock(); + _setting = new Mock(); + + _processor = new StartupProcessor(_logger.Object, _repo.Object, _audioProvider.Object, _setting.Object); + } + + [Fact] + public async Task CanStartAsync() + { + // ACT + await _processor.StartAsync(default); + + // ASSERT + _logger.Verify( + log => log.Log( + It.Is(l => l == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString() == $"{nameof(StartupProcessor)} is starting..."), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + } + + [Fact] + public async Task CanStopAsync() + { + // ACT + await _processor.StopAsync(default); + + // ASSERT + _logger.Verify( + log => log.Log( + It.Is(l => l == LogLevel.Information), + It.IsAny(), + It.Is((v, t) => v.ToString() == $"{nameof(StartupProcessor)} is stopping..."), + It.IsAny(), + It.Is>((v, t) => true)), + Times.Once); + } +} \ No newline at end of file diff --git a/Edge.MyMusic/src/Edge.MyMusic/ApplicationBuilderExtension.cs b/Edge.MyMusic/src/Edge.MyMusic/ApplicationBuilderExtension.cs index a27ff7e..43073e1 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/ApplicationBuilderExtension.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/ApplicationBuilderExtension.cs @@ -57,6 +57,11 @@ internal static IApplicationBuilder UseCors(this IApplicationBuilder application internal static IApplicationBuilder UseServerHandler(this IApplicationBuilder application) { + JsonSerializerOptions options = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + return application.UseExceptionHandler(app => { app.Run(async context => @@ -77,10 +82,7 @@ await context.Response.WriteAsync(JsonSerializer.Serialize Type = exceptionHandlerPathFeature.Error.GetType().Name, exceptionHandlerPathFeature.Error.Message }, - new JsonSerializerOptions() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - } + options )); } } diff --git a/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPostRequest.cs b/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPostRequest.cs index 7a55170..d7256e7 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPostRequest.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPostRequest.cs @@ -24,7 +24,7 @@ public IEnumerable Validate(ValidationContext validationContex }); } - internal IEnumerable Validate(IReadOnlyDictionary properties) + private IEnumerable Validate(IReadOnlyDictionary properties) { if (!Uri.IsWellFormedUriString(this.Path, UriKind.Absolute)) { diff --git a/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPutRequest.cs b/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPutRequest.cs index b23fb44..ae07397 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPutRequest.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPutRequest.cs @@ -25,7 +25,7 @@ public IEnumerable Validate(ValidationContext validationContex }); } - internal IEnumerable Validate(IReadOnlyDictionary properties) + private IEnumerable Validate(IReadOnlyDictionary properties) { if (!string.IsNullOrEmpty(this.Path) && !Uri.IsWellFormedUriString(this.Path, UriKind.Absolute)) diff --git a/Edge.MyMusic/src/Edge.MyMusic/Processors/BaseProcessor.cs b/Edge.MyMusic/src/Edge.MyMusic/Processors/BaseProcessor.cs index 99588b8..fa69815 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Processors/BaseProcessor.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Processors/BaseProcessor.cs @@ -1,6 +1,6 @@ namespace Edge.MyMusic.Processors; -internal abstract class BaseProcessor: IHostedLifecycleService +public abstract class BaseProcessor: IHostedLifecycleService { public virtual Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask; public virtual Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; diff --git a/Edge.MyMusic/src/Edge.MyMusic/Processors/StartupProcessor.cs b/Edge.MyMusic/src/Edge.MyMusic/Processors/StartupProcessor.cs index 8277cf0..8f8b5a4 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Processors/StartupProcessor.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Processors/StartupProcessor.cs @@ -6,13 +6,13 @@ namespace Edge.MyMusic.Processors; -internal class StartupProcessor( +public class StartupProcessor( ILogger logger, IMusicRepository musicRepository, IAudioProvider audioProvider, IArgsSetting argsSetting) : BaseProcessor { - private readonly ILogger _logger = logger; + private readonly ILogger _logger = logger; private readonly IMusicRepository _musicRepository = musicRepository; private readonly IAudioProvider _audioProvider = audioProvider; private readonly IArgsSetting _argsSetting = argsSetting; @@ -32,19 +32,22 @@ public override Task StartedAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - return Task.Run(async() => + try { - IEnumerable files = Directory.EnumerateFiles(_argsSetting.AudiosPath); - - foreach(string filePath in files) + return Parallel.ForEachAsync( + Directory.EnumerateFiles(_argsSetting.AudiosPath), + new ParallelOptions { MaxDegreeOfParallelism = 3, + CancellationToken = cancellationToken }, async (file, ct) => { - string path = $"{_argsSetting.BaseUrl}/audios/{Path.GetFileName(filePath)}"; + ct.ThrowIfCancellationRequested(); + + string path = $"{_argsSetting.BaseUrl}/audios/{Path.GetFileName(file)}"; AudioResponse? audio = await _audioProvider.GetMetadataAsync(path); if (audio == null) { - _logger.LogWarning($"Unable to find audio file {path}", new[] { path}); - continue; + _logger.LogWarning($"Unable to find audio file", new[] { path }); + return; } await _musicRepository.AddMusicAsync(new MusicDocument @@ -56,8 +59,13 @@ await _musicRepository.AddMusicAsync(new MusicDocument Path = path, Title = audio.Title ?? _default }); - }; - }); + }); + } + catch(OperationCanceledException ex) + { + _logger.LogWarning("Operation was cancelled", new[] { ex.Message }); + return Task.CompletedTask; + } } public override Task StopAsync(CancellationToken cancellationToken) diff --git a/Edge.MyMusic/src/Edge.MyMusic/Services/MapperExtension.cs b/Edge.MyMusic/src/Edge.MyMusic/Services/MapperExtension.cs index 88e03a6..a127b4c 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Services/MapperExtension.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Services/MapperExtension.cs @@ -13,7 +13,7 @@ public static MusicDocument ToDocument(this SongPostRequest from, int? duration) Album = from.Album, Artist = from.Artist, IsFavorite = from.IsFavorite, - Length = duration ?? 0, + Length = duration.GetValueOrDefault(-1), Path = from.Path, Title = from.Title }; diff --git a/Edge.MyMusic/src/Edge.MyMusic/Services/MusicService.cs b/Edge.MyMusic/src/Edge.MyMusic/Services/MusicService.cs index 0782244..9f285ab 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Services/MusicService.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Services/MusicService.cs @@ -17,14 +17,7 @@ public async Task> SearchSongsAsync(SongSearchRequ public async Task AddSongAsync(SongPostRequest request) { - try - { - return (await _musicRepository.AddMusicAsync(request.ToDocument((await _audioProvider.GetMetadataAsync(request.Path))?.Duration))).ToResponse(); - } - catch - { - throw new Exception("Unable to add song"); - } + return (await _musicRepository.AddMusicAsync(request.ToDocument((await _audioProvider.GetMetadataAsync(request.Path))?.Duration))).ToResponse(); } public async Task GetSongAsync(string id) diff --git a/Edge.MyMusic/src/Edge.MyMusic/Settings/ApplicationSetting.cs b/Edge.MyMusic/src/Edge.MyMusic/Settings/ApplicationSetting.cs index 4e702cb..ae4f18c 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Settings/ApplicationSetting.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Settings/ApplicationSetting.cs @@ -1,6 +1,6 @@ namespace Edge.MyMusic.Settings; -internal class ApplicationSetting : ICORSPolicySetting, IArgsSetting, IMongoDBSetting +internal class ApplicationSetting : IApplicationSetting { public string? PolicyName { get; init; } public string[]? AllowedOrigins { get; init; } diff --git a/Edge.MyMusic/src/Edge.MyMusic/Settings/IApplicationSetting.cs b/Edge.MyMusic/src/Edge.MyMusic/Settings/IApplicationSetting.cs new file mode 100644 index 0000000..98a5ab8 --- /dev/null +++ b/Edge.MyMusic/src/Edge.MyMusic/Settings/IApplicationSetting.cs @@ -0,0 +1,3 @@ +namespace Edge.MyMusic.Settings; + +public interface IApplicationSetting : ICORSPolicySetting, IArgsSetting, IMongoDBSetting; \ No newline at end of file diff --git a/Edge.MyMusic/src/Edge.MyMusic/Settings/IArgsSetting.cs b/Edge.MyMusic/src/Edge.MyMusic/Settings/IArgsSetting.cs index b1e1266..4ac085c 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Settings/IArgsSetting.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Settings/IArgsSetting.cs @@ -1,6 +1,6 @@ namespace Edge.MyMusic.Settings; -internal interface IArgsSetting +public interface IArgsSetting { string? AudiosPath { get; init; } string? BaseUrl { get; init; } diff --git a/Edge.MyMusic/src/Edge.MyMusic/Settings/ICORSPolicySetting.cs b/Edge.MyMusic/src/Edge.MyMusic/Settings/ICORSPolicySetting.cs index b0869e2..d7e8050 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Settings/ICORSPolicySetting.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Settings/ICORSPolicySetting.cs @@ -1,6 +1,6 @@ namespace Edge.MyMusic.Settings; -internal interface ICORSPolicySetting +public interface ICORSPolicySetting { string? PolicyName { get; init; } string[]? AllowedOrigins { get; init; } diff --git a/Edge.MyMusic/src/Edge.MyMusic/Settings/IMongoDBSetting.cs b/Edge.MyMusic/src/Edge.MyMusic/Settings/IMongoDBSetting.cs index 7412488..b2070dd 100644 --- a/Edge.MyMusic/src/Edge.MyMusic/Settings/IMongoDBSetting.cs +++ b/Edge.MyMusic/src/Edge.MyMusic/Settings/IMongoDBSetting.cs @@ -1,6 +1,6 @@ namespace Edge.MyMusic.Settings; -internal interface IMongoDBSetting +public interface IMongoDBSetting { string? ConnectionString { get; init; } string? DatabaseName { get; init; } diff --git a/Web.MyMusic/src/shared/components/nav-sidebar.tsx b/Web.MyMusic/src/shared/components/nav-sidebar.tsx index c1ef200..c49aa68 100644 --- a/Web.MyMusic/src/shared/components/nav-sidebar.tsx +++ b/Web.MyMusic/src/shared/components/nav-sidebar.tsx @@ -21,7 +21,7 @@ const StyledListItemButton = styled(ListItemButton)({ backgroundColor: Color.NeonPink, color: "white", "& .MuiSvgIcon-root": { - color: Color.White, // Change the color of the icon on hover + color: Color.White, }, }, });