Skip to content

Commit df2f399

Browse files
authored
v10.0.0
Release v10.0.0
2 parents 56761e7 + cf1f9cf commit df2f399

File tree

8 files changed

+313
-4
lines changed

8 files changed

+313
-4
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
<Solution>
2+
<Folder Name="/assets/">
3+
<File Path="Build.ps1" />
4+
<File Path="Directory.Build.props" />
5+
<File Path="Directory.Version.props" />
6+
<File Path="LICENSE" />
7+
</Folder>
8+
<Folder Name="/tests/">
9+
<Project Path="tests/FileValidator.Extensions.DependencyInjection.Tests.Unit/FileValidator.Extensions.DependencyInjection.Tests.Unit.csproj" />
10+
</Folder>
211
<Project Path="src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj" />
312
</Solution>

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,83 @@
11
# ByteGuard.FileValidator.Extensions.DependencyInjection ![NuGet Version](https://img.shields.io/nuget/v/ByteGuard.FileValidator.Extensions.DependencyInjection)
22

3+
`ByteGuard.FileValidator.Extensions.DependencyInjection` provides first-class integration of `ByteGuard.FileValidator` with `Microsoft.Extensions.DependencyInjection`.
4+
5+
It gives you:
6+
- Extension methods to register the file validator in the DI container
7+
- Easy configuration via appsettings.json or fluent configuration in code
8+
9+
> This package is the `Microsoft.Extensions.DependencyInjection` integration layer.
10+
> The core validation logic lives in [`ByteGuard.FileValidator`](https://github.com/ByteGuard-HQ/byteguard-file-validator-net).
11+
12+
## Getting Started
13+
14+
### Installation
15+
This package is published and installed via NuGet.
16+
17+
Reference the package in your project:
18+
```bash
19+
dotnet add package ByteGuard.FileValidator.Extensions.DependencyInjection
20+
```
21+
22+
## Usage
23+
24+
### Add to DI container
25+
In your `Program.cs` (or `Startup.cs` in older projects), register the validator:
26+
27+
```csharp
28+
using ByteGuard.FileValidator;
29+
using ByteGuard.FileValidator.Extensions.DependencyInjection;
30+
31+
// Using inline configuration
32+
builder.Services.AddFileValidator(options =>
33+
{
34+
options.AllowFileTypes(FileExtensions.Pdf, FileExtensions.Jpg, FileExtensions.Png);
35+
options.FileSizeLimit = ByteSize.MegaBytes(25);
36+
options.ThrowOnInvalidFiles(false);
37+
});
38+
39+
// Using configuration from appsettings.json
40+
builder.Services.AddFileValidator(options => configuration.GetSection("FileValidatorConfiguration").Bind(options));
41+
```
42+
43+
### Injection & Usage
44+
You can then inject `FileValidator` into your services and other classes.
45+
46+
```csharp
47+
public class MyService
48+
{
49+
private readonly FileValidator _fileValidator;
50+
51+
public MyService(FileValidator fileValidator)
52+
{
53+
_fileValidator = fileValidator;
54+
}
55+
56+
public bool SaveFile(Stream fileStream, string fileName)
57+
{
58+
var isValid = _fileValidator.IsValidFile(fileName, fileStream);
59+
60+
// ...
61+
}
62+
}
63+
```
64+
65+
### Configuration via appsettings
66+
It's possible to configure the `FileValidator` through `appsettings.json`.
67+
68+
> _ℹ️ As you'll notice below, you can either define the `FileSizeLimit` in raw byte size, or use the `UnitFileSizeLimit` to define
69+
> the file size in a more human readable format. When both are defined, `FileSizeLimit` always wins over `UnitFileSizeLimit`._
70+
71+
```json
72+
{
73+
"FileValidatorConfiguration": {
74+
"SupportedFileTypes": [ ".pdf", ".jpg", ".png" ],
75+
"FileSizeLimit": 26214400,
76+
"UnitFileSizeLimit": "25MB",
77+
"ThrowExceptionOnInvalidFile": true
78+
}
79+
}
80+
```
81+
82+
## License
83+
_ByteGuard.FileValidator.Extensions.DpendencyInjection is Copyright © ByteGuard Contributors - Provided under the MIT license._

src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
5-
<Authors>ByteGuard Contributors, detilium</Authors>
6-
<Description>ByteGuard File Validator support for ASP.NET Core.</Description>
5+
<Authors>ByteGuard Contributors</Authors>
6+
<Description>ByteGuard File Validator support for Microsoft.Extensions.DependencyInjection.</Description>
77
<PackageProjectUrl>https://github.com/ByteGuard-HQ/byteguard-file-validator-extensions-dependency-injection</PackageProjectUrl>
88
<RepositoryUrl>https://github.com/ByteGuard-HQ/byteguard-file-validator-extensions-dependency-injection</RepositoryUrl>
99
<RepositoryType>git</RepositoryType>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using ByteGuard.FileValidator.Configuration;
2+
using Microsoft.Extensions.Options;
3+
4+
namespace ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
5+
6+
/// <summary>
7+
/// File validator configuration options validator for use with the options pattern.
8+
/// </summary>
9+
public class FileValidatorConfigurationOptionsValidator : IValidateOptions<FileValidatorConfiguration>
10+
{
11+
/// <inheritdoc />
12+
public ValidateOptionsResult Validate(string? name, FileValidatorConfiguration config)
13+
{
14+
try
15+
{
16+
ConfigurationValidator.ThrowIfInvalid(config);
17+
return ValidateOptionsResult.Success;
18+
}
19+
catch (Exception ex)
20+
{
21+
return ValidateOptionsResult.Fail(ex.Message);
22+
}
23+
}
24+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,47 @@
11
namespace ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
22

3+
/// <summary>
4+
/// Appsettings specific configuration for the File Validator.
5+
/// </summary>
36
public class FileValidatorSettingsConfiguration
47
{
8+
/// <summary>
9+
/// Supported file types.
10+
/// </summary>
11+
/// <remarks>
12+
/// Setup which file types are supported by the file validator.
13+
/// File types should be specified with their leading dot, e.g. <c>.jpg</c>, <c>.png</c>, <c>.pdf</c>, etc.
14+
/// Currently supported file types can be found in <see cref="FileExtensions"/>.
15+
/// </remarks>
16+
public List<string> SupportedFileTypes { get; set; } = new List<string>();
517

18+
/// <summary>
19+
/// Maximum file size limit in bytes.
20+
/// </summary>
21+
/// <remarks>
22+
/// Defines the file size limit of files. See <see cref="ByteSize"/> for conversion help.
23+
/// </remarks>
24+
public long FileSizeLimit { get; set; } = -1;
25+
26+
/// <summary>
27+
/// Maximum file size limit in unit string representation (e.g. "25MB", "2 GB", etc.).
28+
/// </summary>
29+
/// <remarks>
30+
/// Will be ignored if <see cref="FileSizeLimit"/> is defined.
31+
/// Spacing (<c>"25 MB"</c> vs. <c>"25MB"</c>) is irrelevant.
32+
/// <para>Supported string representation are:
33+
/// <ul>
34+
/// <li><c>B</c>: Bytes</li>
35+
/// <li><c>KB</c>: Kilobytes</li>
36+
/// <li><c>MB</c>: Megabytes</li>
37+
/// <li><c>GB</c>: Gigabytes</li>
38+
/// </ul>
39+
/// </para>
40+
/// </remarks>
41+
public string? UnitFileSizeLimit { get; set; }
42+
43+
/// <summary>
44+
/// Whether to throw an exception if an unsupported/invalid file is encountered. Defaults to <c>true</c>.
45+
/// </summary>
46+
public bool ThrowExceptionOnInvalidFile { get; set; } = true;
647
}

src/ByteGuard.FileValidator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,52 @@
1+
using ByteGuard.FileValidator.Configuration;
2+
using ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
13
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Options;
25

36
namespace ByteGuard.FileValidator.Extensions.DependencyInjection;
47

8+
/// <summary>
9+
/// Extension methods for registering the File Validator services in an <see cref="IServiceCollection"/>.
10+
/// </summary>
511
public static class ServiceCollectionExtensions
612
{
7-
public static IServiceCollection AddFileValidator(this IServiceCollection services, Action<Configuration.FileValidatorSettingsConfiguration> configureSettings)
13+
/// <summary>
14+
/// Adds the File Validator services to the specified <see cref="IServiceCollection"/> with custom configuration options.
15+
/// </summary>
16+
/// <param name="services">Service collection.</param>
17+
/// <param name="options">Configuration options.</param>
18+
public static IServiceCollection AddFileValidator(this IServiceCollection services, Action<FileValidatorSettingsConfiguration> options)
819
{
9-
// TODO.
20+
// Validate and setup configuration options.
21+
services.AddSingleton<IValidateOptions<FileValidatorConfiguration>,
22+
FileValidatorConfigurationOptionsValidator>();
23+
24+
services.Configure(options);
25+
26+
services.AddOptions<FileValidatorConfiguration>()
27+
.Configure<IOptions<FileValidatorSettingsConfiguration>>((cfg, settings) =>
28+
{
29+
// Convert from FileValidatorSettingsConfiguration to FileValidatorConfiguration.
30+
cfg.SupportedFileTypes = settings.Value.SupportedFileTypes;
31+
cfg.ThrowExceptionOnInvalidFile = settings.Value.ThrowExceptionOnInvalidFile;
32+
33+
if (settings.Value.FileSizeLimit != -1)
34+
{
35+
cfg.FileSizeLimit = settings.Value.FileSizeLimit;
36+
}
37+
else if (!string.IsNullOrWhiteSpace(settings.Value.UnitFileSizeLimit))
38+
{
39+
cfg.FileSizeLimit = ByteSize.Parse(settings.Value.UnitFileSizeLimit);
40+
}
41+
})
42+
.ValidateOnStart();
43+
44+
// Register the FileValidator service.
45+
services.AddSingleton(serviceProvider =>
46+
{
47+
var configuration = serviceProvider.GetRequiredService<IOptions<FileValidatorConfiguration>>().Value;
48+
return new FileValidator(configuration);
49+
});
1050

1151
return services;
1252
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<GenerateDocumentationFile>false</GenerateDocumentationFile>
6+
<IsTestProject>true</IsTestProject>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="coverlet.collector" Version="6.0.0" />
11+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
13+
<PackageReference Include="NSubstitute" Version="5.3.0" />
14+
<PackageReference Include="xunit" Version="2.5.3" />
15+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<Using Include="Xunit" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="../../src/ByteGuard.FileValidator.Extensions.DependencyInjection/ByteGuard.FileValidator.Extensions.DependencyInjection.csproj" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using ByteGuard.FileValidator.Configuration;
2+
using ByteGuard.FileValidator.Extensions.DependencyInjection.Configuration;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Options;
5+
6+
namespace ByteGuard.FileValidator.Extensions.DependencyInjection.Tests.Unit;
7+
8+
public class ServiceCollectionExtensionsTests
9+
{
10+
[Fact(DisplayName = "AddFileValidator adds FileValidator to the service collection")]
11+
public void AddFileValidator_AddsFileValidatorToServiceCollection()
12+
{
13+
// Arrange
14+
var services = new ServiceCollection();
15+
16+
// Act
17+
services.AddFileValidator(config =>
18+
{
19+
config.SupportedFileTypes = new List<string>() { ".png", ".jpg" };
20+
config.UnitFileSizeLimit = "25MB";
21+
});
22+
23+
var serviceProvider = services.BuildServiceProvider();
24+
25+
// Assert
26+
var fileValidator = serviceProvider.GetService<FileValidator>();
27+
Assert.NotNull(fileValidator);
28+
}
29+
30+
[Fact(DisplayName = "AddFileValidator should throws exception when configuration is invalid")]
31+
public void AddFileValidator_ShouldThrowException_WhenConfigurationIsInvalid()
32+
{
33+
// Arrange
34+
var services = new ServiceCollection();
35+
36+
// Act
37+
services.AddFileValidator(config =>
38+
{
39+
config.SupportedFileTypes = []; // Invalid configuration - missing supported file types
40+
});
41+
42+
var serviceProvider = services.BuildServiceProvider();
43+
44+
// Assert
45+
Assert.ThrowsAny<Exception>(() => serviceProvider.GetRequiredService<FileValidator>());
46+
}
47+
48+
[Fact(DisplayName = "AddFileValidator should set correct file size limit when using the friendly file size limit property")]
49+
public void AddFileValidator_FriendlyFileSizeLimitSet_ShouldAddAFileValidator()
50+
{
51+
// Arrange
52+
var services = new ServiceCollection();
53+
Action<FileValidatorSettingsConfiguration> configAction = options =>
54+
{
55+
options.SupportedFileTypes = [".pdf"];
56+
options.UnitFileSizeLimit = "25MB";
57+
};
58+
59+
// Act
60+
services.AddFileValidator(configAction);
61+
62+
// Assert
63+
var sp = services.BuildServiceProvider();
64+
var options = sp.GetRequiredService<IOptions<FileValidatorConfiguration>>();
65+
Assert.Equal(ByteSize.MegaBytes(25), options.Value.FileSizeLimit);
66+
}
67+
68+
[Fact(DisplayName = "AddFileValidator should use file size limit over friendly file size limit if both are set")]
69+
public void AddFileValidator_FileSizeLimitAndFriendlyFileSizeLimitBothSet_ShouldUseFileSizeLimitOverFriendlyFileSizeLimit()
70+
{
71+
// Arrange
72+
var services = new ServiceCollection();
73+
Action<FileValidatorSettingsConfiguration> configAction = options =>
74+
{
75+
options.SupportedFileTypes = [".pdf"];
76+
options.FileSizeLimit = ByteSize.MegaBytes(10);
77+
options.UnitFileSizeLimit = "25MB";
78+
};
79+
80+
// Act
81+
services.AddFileValidator(configAction);
82+
83+
// Assert
84+
var sp = services.BuildServiceProvider();
85+
var options = sp.GetRequiredService<IOptions<FileValidatorConfiguration>>();
86+
Assert.Equal(ByteSize.MegaBytes(10), options.Value.FileSizeLimit);
87+
}
88+
}

0 commit comments

Comments
 (0)