diff --git a/sample/Sample.Minimal/CustomHostedService.cs b/sample/Sample.Minimal/CustomHostedService.cs deleted file mode 100644 index 7b7f090a8..000000000 --- a/sample/Sample.Minimal/CustomHostedService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.Options; - -internal class CustomHostedService(IOptions options) : BackgroundService -{ - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - // ReSharper disable once UnusedVariable - var v = options.Value.A; - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/sample/Sample.Minimal/CustomHostedServiceOptions.cs b/sample/Sample.Minimal/CustomHostedServiceOptions.cs deleted file mode 100644 index c3c34467c..000000000 --- a/sample/Sample.Minimal/CustomHostedServiceOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using FluentValidation; - -internal class CustomHostedServiceOptions -{ - public string? A { get; set; } - - [UsedImplicitly] - private sealed class Validator : AbstractValidator - { - public Validator() - { - RuleFor(z => z.A).NotNull(); - } - } -} \ No newline at end of file diff --git a/sample/Sample.Minimal/Program.cs b/sample/Sample.Minimal/Program.cs index 279d37e33..6af6f3808 100644 --- a/sample/Sample.Minimal/Program.cs +++ b/sample/Sample.Minimal/Program.cs @@ -8,7 +8,6 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers().AddControllersAsServices(); -builder.Services.AddHostedService(); var app = await builder.ConfigureRocketSurgery(); app.UseExceptionHandler(); diff --git a/sample/Sample.Restful/Program.cs b/sample/Sample.Restful/Program.cs index d5332ab59..a66d29b6f 100644 --- a/sample/Sample.Restful/Program.cs +++ b/sample/Sample.Restful/Program.cs @@ -1,7 +1,4 @@ -using FluentValidation; - using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Options; using Rocket.Surgery.Hosting; using Rocket.Surgery.LaunchPad.AspNetCore; @@ -11,7 +8,6 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers().AddControllersAsServices(); -builder.Services.AddHostedService(); var app = await builder.ConfigureRocketSurgery(); app.UseExceptionHandler(); @@ -53,24 +49,3 @@ app.Run(); public partial class Program; - -internal class CustomHostedServiceOptions -{ - public string? A { get; set; } - - [UsedImplicitly] - private sealed class Validator : AbstractValidator - { - public Validator() => RuleFor(z => z.A).NotNull(); - } -} - -internal class CustomHostedService(IOptions options) : BackgroundService -{ - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - // ReSharper disable once UnusedVariable - var v = options.Value.A; - return Task.CompletedTask; - } -} diff --git a/src/Foundation/Validation/CustomHealthCheckService.cs b/src/Foundation/Validation/CustomHealthCheckService.cs deleted file mode 100644 index 36a3e73c1..000000000 --- a/src/Foundation/Validation/CustomHealthCheckService.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace Rocket.Surgery.LaunchPad.Foundation.Validation; - -internal class CustomHealthCheckService(HealthCheckService wrappedService, ValidationHealthCheckResults healthCheckResults) - : HealthCheckService -{ - public override async Task CheckHealthAsync( - Func? predicate, CancellationToken cancellationToken = new CancellationToken() - ) - { - var results = await wrappedService.CheckHealthAsync(predicate, cancellationToken); - return new HealthReport( - results.Entries.Concat(healthCheckResults.Results).ToDictionary(z => z.Key, z => z.Value), - results.TotalDuration - ); - } -} diff --git a/src/Foundation/Validation/FluentValidationOptions.cs b/src/Foundation/Validation/FluentValidationOptions.cs index 9bee2a4dc..50af15126 100644 --- a/src/Foundation/Validation/FluentValidationOptions.cs +++ b/src/Foundation/Validation/FluentValidationOptions.cs @@ -1,5 +1,7 @@ using FluentValidation; + using Microsoft.Extensions.Options; + using Rocket.Surgery.LaunchPad.Foundation.Extensions; namespace Rocket.Surgery.LaunchPad.Foundation.Validation; @@ -8,22 +10,18 @@ namespace Rocket.Surgery.LaunchPad.Foundation.Validation; /// This class enables fluent validators to be used for options validations! /// /// -internal class FluentValidationOptions( - ValidationHealthCheckResults? healthCheckResults = null, - IValidator? validator = null -) +internal class FluentValidationOptions(IValidator? validator = null) : IValidateOptions where T : class { public virtual ValidateOptionsResult Validate(string? name, T options) { - if (validator == null) return ValidateOptionsResult.Skip; + if (validator is null) return ValidateOptionsResult.Skip; var result = validator.Validate(options); - healthCheckResults?.AddResult(typeof(T).GetNestedTypeName(), name ?? Options.DefaultName, result); - if (result.IsValid) return ValidateOptionsResult.Success; - - return ValidateOptionsResult.Fail( + return result.IsValid + ? ValidateOptionsResult.Success + : ValidateOptionsResult.Fail( new[] { $"Failure while validating {typeof(T).GetNestedTypeName()}{( name == Options.DefaultName ? "" : $" (Name: {name})" )}." } .Concat(result.Errors.Select(z => z.ToString())) ); diff --git a/src/Foundation/Validation/HealthCheckFluentValidationOptions.cs b/src/Foundation/Validation/HealthCheckFluentValidationOptions.cs deleted file mode 100644 index 59a6ee0cf..000000000 --- a/src/Foundation/Validation/HealthCheckFluentValidationOptions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FluentValidation; -using Microsoft.Extensions.Options; -using Rocket.Surgery.LaunchPad.Foundation.Extensions; - -namespace Rocket.Surgery.LaunchPad.Foundation.Validation; - -/// -/// This class enables fluent validators to be used for options validations! -/// -/// -internal class HealthCheckFluentValidationOptions( - ValidationHealthCheckResults healthCheckResults, - IValidator? validator = null -) -#pragma warning disable CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well. - : FluentValidationOptions(null, validator) -#pragma warning restore CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well. - where T : class -{ - /* null because we're adding results during the validate call here */ - - public override ValidateOptionsResult Validate(string? name, T options) - { - if (validator == null) return ValidateOptionsResult.Skip; - - var result = validator.Validate(options); - healthCheckResults.AddResult(typeof(T).GetNestedTypeName(), name ?? Options.DefaultName, result); - return healthCheckResults.ApplicationHasStarted ? base.Validate(name, options) : ValidateOptionsResult.Skip; - } -} diff --git a/src/Foundation/Validation/ValidationHealthCheckResults.cs b/src/Foundation/Validation/ValidationHealthCheckResults.cs deleted file mode 100644 index f733d4ed5..000000000 --- a/src/Foundation/Validation/ValidationHealthCheckResults.cs +++ /dev/null @@ -1,59 +0,0 @@ -using FluentValidation; -using FluentValidation.Results; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Options; - -namespace Rocket.Surgery.LaunchPad.Foundation.Validation; - -internal class ValidationHealthCheckResults -{ - private readonly Dictionary _results = new(); - - public ValidationHealthCheckResults(IHostApplicationLifetime hostApplicationLifetime) - { - hostApplicationLifetime.ApplicationStarted.Register(() => ApplicationHasStarted = true); - } - - public IEnumerable> Results => _results; - public bool ApplicationHasStarted { get; internal set; } - - public void AddResult(string optionsTypeName, string optionsName, ValidationResult result) - { - var key = optionsTypeName; - if (optionsName != Options.DefaultName) key += $"_{optionsName}"; - - if (result.IsValid) - _results[key] = - new( - HealthStatus.Healthy, - $"Options Validation {key}", - TimeSpan.Zero, - null, - result - .Errors - .GroupBy(z => z.PropertyName) - .ToDictionary( - z => z.Key, - z => (object)z.Select(x => x.ToString()).ToArray() - ), - new[] { "options-validation", key, } - ); - else - _results[key] = - new( - HealthStatus.Unhealthy, - $"Options Validation {key}", - TimeSpan.Zero, - new ValidationException(result.Errors), - result - .Errors - .GroupBy(z => z.PropertyName) - .ToDictionary( - z => z.Key, - z => (object)z.Select(x => x.ToString()).ToArray() - ), - new[] { "options-validation", "Options Validation", key, optionsTypeName, } - ); - } -} \ No newline at end of file diff --git a/src/Hosting/Conventions/SerilogTelemetryConvention.cs b/src/Hosting/Conventions/SerilogTelemetryConvention.cs index 2cd5af825..5641af95e 100644 --- a/src/Hosting/Conventions/SerilogTelemetryConvention.cs +++ b/src/Hosting/Conventions/SerilogTelemetryConvention.cs @@ -16,6 +16,7 @@ namespace Rocket.Surgery.LaunchPad.Hosting.Conventions; public partial class SerilogTelemetryConvention : ISerilogConvention { /// - [RequiresUnreferencedCode()] - public void Register(IConventionContext context, IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration) => loggerConfiguration.WriteTo.OpenTelemetry(_ => { }, configuration.GetValue); + [RequiresUnreferencedCode("Serilog")] + public void Register(IConventionContext context, IConfiguration configuration, IServiceProvider services, LoggerConfiguration loggerConfiguration) => + loggerConfiguration.WriteTo.OpenTelemetry(_ => { }, configuration.GetValue); } diff --git a/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs b/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs index d8fbf70dd..a73c507f7 100644 --- a/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs +++ b/test/Extensions.Tests/Validation/HealthCheckOptionsValidationTests.cs @@ -7,39 +7,17 @@ using Rocket.Surgery.Conventions; using Rocket.Surgery.LaunchPad.Foundation; -using Rocket.Surgery.LaunchPad.Foundation.Validation; namespace Extensions.Tests.Validation; public class HealthCheckOptionsValidationTests(ITestOutputHelper outputHelper) : AutoFakeTest(XUnitDefaults.CreateTestContext(outputHelper)), IAsyncLifetime { [Fact] - public async Task Should_Validate_Options_And_Throw() + public Task Should_Validate_Options_And_Throw() { var a = () => Container.Resolve>().Value; - a.Should().NotThrow(); - await Verify(Container.Resolve().Results); - } - - [Fact] - public async Task Should_Validate_Options_And_Pass() - { - var services = new ServiceCollection(); - services - .AddOptions() - .Configure( - options => - { - options.Bool = true; - options.Double = -50; - options.Int = 50; - options.String = "Hello"; - } - ); - Populate(services); - var a = () => Container.Resolve>().Value; - a.Should().NotThrow(); - await Verify(Container.Resolve().Results); + a.Should().Throw(); + return Task.CompletedTask; } [Fact] @@ -59,14 +37,12 @@ public async Task Should_Validate_Options_And_Throw_If_Out_Of_Bounds() ); Populate(services); var a = () => Container.Resolve>().Value; - a.Should().NotThrow(); - await Verify(Container.Resolve().Results); + a.Should().Throw(); } [Fact] public async Task Should_Validate_Options_And_Throw_After_Application_Has_Started() { - Container.Resolve().ApplicationHasStarted = true; var a = () => Container.Resolve>().Value; var failures = a .Should() @@ -78,7 +54,6 @@ public async Task Should_Validate_Options_And_Throw_After_Application_Has_Starte [Fact] public void Should_Validate_Options_And_Pass_After_Application_Has_Started() { - Container.Resolve().ApplicationHasStarted = true; var services = new ServiceCollection(); services .AddOptions() @@ -99,7 +74,6 @@ public void Should_Validate_Options_And_Pass_After_Application_Has_Started() [Fact] public async Task Should_Validate_Options_And_Throw_If_Out_Of_Bounds_After_Application_Has_Started() { - Container.Resolve().ApplicationHasStarted = true; var services = new ServiceCollection(); services .AddOptions()