Skip to content

Commit

Permalink
Upgrade to Net8, Update StartupProcessor, use ConcurrentDictionary fo…
Browse files Browse the repository at this point in the history
…r inmemory datastore, and fix security vulnerability
  • Loading branch information
pacna committed Jan 29, 2024
1 parent dcf21d7 commit f51338d
Show file tree
Hide file tree
Showing 35 changed files with 430 additions and 401 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dotnet-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- working-directory: ./Edge.MyMusic
run: |
dotnet test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ obj
bin
out

## wwwroot
Edge.MyMusic/src/Edge.MyMusic/wwwroot/*
!Edge.MyMusic/src/Edge.MyMusic/wwwroot/__blank__


# Web.MyMusic

Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic/bin/Debug/net7.0/Edge.MyMusic.dll",
"program": "${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic/bin/Debug/net8.0/Edge.MyMusic.dll",
"args": [],
"cwd": "${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic",
"stopAtEntry": false,
Expand Down
6 changes: 3 additions & 3 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic/Edge.MyMusic.csproj",
"${workspaceFolder}/Edge.MyMusic/Edge.MyMusic.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
Expand All @@ -19,7 +19,7 @@
"type": "process",
"args": [
"publish",
"${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic/Edge.MyMusic.csproj",
"${workspaceFolder}/Edge.MyMusic/Edge.MyMusic.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
Expand All @@ -33,7 +33,7 @@
"watch",
"run",
"--project",
"${workspaceFolder}/Edge.MyMusic/src/Edge.MyMusic/Edge.MyMusic.csproj"
"${workspaceFolder}/Edge.MyMusic/Edge.MyMusic.sln"
],
"problemMatcher": "$msCompile"
}
Expand Down
6 changes: 6 additions & 0 deletions Edge.MyMusic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ $ dotnet run -webapp
$ dotnet run --audios=path/to/folder/or/file
```

- `--base-url`: Specifies the base URL for the server. If not provided, the default is `http://localhost:5000`.

```bash
$ dotnet run --base-url=https://www.mymusic.com
```

## How to Run Tests

Execute the following command:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand Down
7 changes: 5 additions & 2 deletions Edge.MyMusic/src/Edge.MyMusic.Tests/MusicServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ public async Task CanAddSongAsync()
IsFavorite = false
};

AudioResponse expectedMetaData = new(title: request.Title, album: request.Album, artist: request.Artist)
AudioResponse expectedMetaData = new()
{
Title = request.Title,
Album = request.Album,
Artist = request.Artist,
Duration = 185
};

Expand Down Expand Up @@ -226,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);
Expand Down
61 changes: 61 additions & 0 deletions Edge.MyMusic/src/Edge.MyMusic.Tests/StartupProcessorTests.cs
Original file line number Diff line number Diff line change
@@ -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<ILogger<StartupProcessor>> _logger;
private readonly Mock<IMusicRepository> _repo;
private readonly Mock<IAudioProvider> _audioProvider;
private readonly Mock<IArgsSetting> _setting;

private readonly StartupProcessor _processor;

public StartupProcessorTests()
{
_logger = new Mock<ILogger<StartupProcessor>>();
_repo = new Mock<IMusicRepository>();
_audioProvider = new Mock<IAudioProvider>();
_setting = new Mock<IArgsSetting>();

_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<LogLevel>(l => l == LogLevel.Information),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == $"{nameof(StartupProcessor)} is starting..."),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
Times.Once);
}

[Fact]
public async Task CanStopAsync()
{
// ACT
await _processor.StopAsync(default);

// ASSERT
_logger.Verify(
log => log.Log(
It.Is<LogLevel>(l => l == LogLevel.Information),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == $"{nameof(StartupProcessor)} is stopping..."),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
Times.Once);
}
}
12 changes: 7 additions & 5 deletions Edge.MyMusic/src/Edge.MyMusic/ApplicationBuilderExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,16 @@ internal static IApplicationBuilder UseCustomPath(this IApplicationBuilder appli

internal static IApplicationBuilder UseCors(this IApplicationBuilder application, ICORSPolicySetting cors)
{
return application.UseCors(cors.PolicyName);
return application.UseCors(cors.PolicyName!);
}

internal static IApplicationBuilder UseServerHandler(this IApplicationBuilder application)
{
JsonSerializerOptions options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

return application.UseExceptionHandler(app =>
{
app.Run(async context =>
Expand All @@ -77,10 +82,7 @@ await context.Response.WriteAsync(JsonSerializer.Serialize
Type = exceptionHandlerPathFeature.Error.GetType().Name,
exceptionHandlerPathFeature.Error.Message
},
new JsonSerializerOptions()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}
options
));
}
}
Expand Down
17 changes: 8 additions & 9 deletions Edge.MyMusic/src/Edge.MyMusic/ApplicationParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,8 @@ public static ApplicationSetting Parse(string[] args, IConfiguration configurati
ICORSPolicySetting? cors = configuration.GetSection("CORSPolicy").Get<ApplicationSetting>();
IMongoDBSetting? dbSetting = configuration.GetSection("MongoDBSetting").Get<ApplicationSetting>();

static bool HasInMemoryFlag(string[] args)
{
return args.Any(arg => arg == "-inmemory");
}

static bool HasWebAppFlag(string[] args)
{
return args.Any(arg => arg == "-webapp");
}
static bool HasInMemoryFlag(string[] args) => args.Any(arg => arg == "-inmemory");
static bool HasWebAppFlag(string[] args) => args.Any(arg => arg == "-webapp");

static string? ExtractAudioFolderPath(string[] args)
{
Expand All @@ -28,13 +21,19 @@ static bool HasWebAppFlag(string[] args)
: flag!.Split("=")[1];
}

static string ExtractBaseUrlPath(string[] args)
{
return args.FirstOrDefault(x => x.StartsWith("--base-url="))?.Split("=")[1] ?? "http://localhost:5000";
}

return new ApplicationSetting
{
PolicyName = cors?.PolicyName,
AllowedOrigins = cors?.AllowedOrigins,
ConnectionString = dbSetting?.ConnectionString,
DatabaseName = dbSetting?.DatabaseName,
AudiosPath = ExtractAudioFolderPath(args),
BaseUrl = ExtractBaseUrlPath(args),
UseInMemory = HasInMemoryFlag(args),
UseWebApp = HasWebAppFlag(args)
};
Expand Down
40 changes: 18 additions & 22 deletions Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPostRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,16 @@

namespace Edge.MyMusic.Controllers.Models;

public sealed class SongPostRequest: IValidatableObject
public sealed class SongPostRequest(
string album,
string artist,
string path,
string title) : IValidatableObject
{
public SongPostRequest(
string album,
string artist,
string path,
string title)
{
this.Album = album;
this.Artist = artist;
this.Path = path;
this.Title = title;
}

public string Album { get; }
public string Artist { get; }
public string Path { get; }
public string Title { get; }
public string Album => album;
public string Artist => artist;
public string Path => path;
public string Title => title;
public bool IsFavorite { get; init; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
Expand All @@ -28,18 +20,22 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
{
{ nameof(this.Album), this.Album},
{ nameof(this.Artist), this.Artist},
{ nameof(this.Path), this.Path},
{ nameof(this.Title), this.Title},
});
}

internal IEnumerable<ValidationResult> Validate(IReadOnlyDictionary<string, string> propertiesAndValues)
private IEnumerable<ValidationResult> Validate(IReadOnlyDictionary<string, string> properties)
{
foreach(KeyValuePair<string, string> kv in propertiesAndValues)
if (!Uri.IsWellFormedUriString(this.Path, UriKind.Absolute))
{
yield return new ValidationResult("Invalid url", new[] { this.Path });
}

foreach(KeyValuePair<string, string> property in properties)
{
if (string.IsNullOrWhiteSpace(kv.Value))
if (string.IsNullOrWhiteSpace(property.Value))
{
yield return new ValidationResult($"{kv.Key} cannot be empty", new[] { kv.Key});
yield return new ValidationResult($"{property.Key} cannot be empty", new[] { property.Key});
}
}
}
Expand Down
21 changes: 9 additions & 12 deletions Edge.MyMusic/src/Edge.MyMusic/Controllers/Models/SongPutRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public sealed class SongPutRequest : IValidatableObject, IMusicUpdateQuery
{
public string? Album { get; init; }
public string? Artist { get; init; }

[Range(1, int.MaxValue)]
public int? Length { get; init; }
public string? Path { get; init; }
public string? Title { get; init; }
Expand All @@ -19,28 +21,23 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
{
{ nameof(this.Album), this.Album},
{ nameof(this.Artist), this.Artist},
{ nameof(this.Path), this.Path},
{ nameof(this.Title), this.Title},
});
}

internal IEnumerable<ValidationResult> Validate(IReadOnlyDictionary<string, string?> propertiesAndValues)
private IEnumerable<ValidationResult> Validate(IReadOnlyDictionary<string, string?> properties)
{
if (this.Length <= 0)

if (!string.IsNullOrEmpty(this.Path) && !Uri.IsWellFormedUriString(this.Path, UriKind.Absolute))
{
yield return new ValidationResult($"{nameof(this.Length)} needs to be greater than 0", new[] { nameof(this.Length)});
yield return new ValidationResult("Invalid url", new[] { this.Path });
}

foreach(KeyValuePair<string, string?> kv in propertiesAndValues)
foreach(KeyValuePair<string, string?> property in properties.Where(kv => kv.Value != null))
{
if (kv.Value == null)
{
continue;
}

if (kv.Value.IsEmptyOrWhiteSpace())
if (property.Value!.IsEmptyOrWhiteSpace())
{
yield return new ValidationResult($"{kv.Key} cannot be empty", new[] { kv.Key});
yield return new ValidationResult($"{property.Key} cannot be empty", new[] { property.Key});
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions Edge.MyMusic/src/Edge.MyMusic/Controllers/SongController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@

namespace Edge.MyMusic.Controllers;

public class SongController : BaseController
public class SongController(IMusicService musicService) : BaseController
{
private readonly IMusicService _musicService;

public SongController(IMusicService musicService)
{
_musicService = musicService;
}
private readonly IMusicService _musicService = musicService;

[HttpGet]
[ProducesResponseType(statusCode: StatusCodes.Status200OK, Type = typeof(CollectionModel<SongResponse>))]
Expand Down
2 changes: 1 addition & 1 deletion Edge.MyMusic/src/Edge.MyMusic/Edge.MyMusic.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
11 changes: 11 additions & 0 deletions Edge.MyMusic/src/Edge.MyMusic/Processors/BaseProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Edge.MyMusic.Processors;

public abstract class BaseProcessor: IHostedLifecycleService
{
public virtual Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public virtual Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
20 changes: 0 additions & 20 deletions Edge.MyMusic/src/Edge.MyMusic/Processors/MapperExtension.cs

This file was deleted.

Loading

0 comments on commit f51338d

Please sign in to comment.