diff --git a/src/Polly.Core/CircuitBreaker/AdvancedCircuitBreakerStrategyOptions.TResult.cs b/src/Polly.Core/CircuitBreaker/AdvancedCircuitBreakerStrategyOptions.TResult.cs index f48a1dd9de5..c64cd363cf9 100644 --- a/src/Polly.Core/CircuitBreaker/AdvancedCircuitBreakerStrategyOptions.TResult.cs +++ b/src/Polly.Core/CircuitBreaker/AdvancedCircuitBreakerStrategyOptions.TResult.cs @@ -52,6 +52,6 @@ public class AdvancedCircuitBreakerStrategyOptions : CircuitBreakerStra /// /// Value must be greater than 0.5 seconds. Defaults to 30 seconds. /// - [TimeSpan("00:00:00.500")] + [Range(typeof(TimeSpan), "00:00:00.500", "1.00:00:00")] public TimeSpan SamplingDuration { get; set; } = CircuitBreakerConstants.DefaultSamplingDuration; } diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs index ae5cb70884d..9684bea3870 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs @@ -30,7 +30,7 @@ public abstract class CircuitBreakerStrategyOptions : ResilienceStrateg /// Value must be greater than 0.5 seconds. /// Defaults to 5 seconds. /// - [TimeSpan("00:00:00.500")] + [Range(typeof(TimeSpan), "00:00:00.500", "1.00:00:00")] public TimeSpan BreakDuration { get; set; } = CircuitBreakerConstants.DefaultBreakDuration; /// diff --git a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs index 9aa3b8981b2..c5cec288b77 100644 --- a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs +++ b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs @@ -60,7 +60,7 @@ public class RetryStrategyOptions : ResilienceStrategyOptions /// Defaults to 2 seconds. /// /// - [TimeSpan("00:00:00", "1.00:00:00")] + [Range(typeof(TimeSpan), "00:00:00", "1.00:00:00")] public TimeSpan BaseDelay { get; set; } = RetryConstants.DefaultBaseDelay; /// diff --git a/src/Polly.Core/Timeout/TimeoutAttribute.cs b/src/Polly.Core/Timeout/TimeoutAttribute.cs deleted file mode 100644 index 3b7f631ef4e..00000000000 --- a/src/Polly.Core/Timeout/TimeoutAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Globalization; - -namespace Polly.Timeout; - -internal sealed class TimeoutAttribute : ValidationAttribute -{ - protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) - { - if (value is TimeSpan timeSpan && !TimeoutUtil.IsTimeoutValid(timeSpan)) - { - return new ValidationResult(string.Format(CultureInfo.InvariantCulture, TimeoutUtil.TimeSpanInvalidMessage, timeSpan)); - } - - return ValidationResult.Success!; - } -} diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index a26babb514e..19365a1cab1 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -32,7 +32,7 @@ protected internal override async ValueTask> ExecuteCoreAsync> ExecuteCoreAsync GenerateTimeoutAsync(ResilienceContext context) - { - var timeout = await TimeoutGenerator!(new TimeoutGeneratorArguments(context)).ConfigureAwait(context.ContinueOnCapturedContext); - if (!TimeoutUtil.IsTimeoutValid(timeout)) - { - return DefaultTimeout; - } - - return timeout; - } - private static CancellationTokenRegistration CreateRegistration(CancellationTokenSource cancellationSource, CancellationToken previousToken) { if (previousToken.CanBeCanceled) diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs index 5dc00b14bc2..e2e81855635 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs @@ -13,7 +13,7 @@ public static class TimeoutResilienceStrategyBuilderExtensions /// /// The builder type. /// The builder instance. - /// The timeout value. This value should be greater than or . + /// The timeout value. This value should be greater than . /// The same builder instance. /// Thrown when is . /// Thrown when the options produced from the arguments are invalid. diff --git a/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs b/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs index 253d5df0bd8..8aa2a6058da 100644 --- a/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs +++ b/src/Polly.Core/Timeout/TimeoutStrategyOptions.cs @@ -1,3 +1,5 @@ +using System.ComponentModel.DataAnnotations; + namespace Polly.Timeout; /// @@ -15,10 +17,10 @@ public class TimeoutStrategyOptions : ResilienceStrategyOptions /// Gets or sets the default timeout. /// /// - /// By default, the value is set to thus making the timeout strategy disabled. + /// Defaults to 30 seconds. This value must be greater than 1 second and less than 24 hours. /// - [Timeout] - public TimeSpan Timeout { get; set; } = System.Threading.Timeout.InfiniteTimeSpan; + [Range(typeof(TimeSpan), "00:00:01", "1.00:00:00")] + public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); /// /// Gets or sets the timeout generator that generates the timeout for a given execution. diff --git a/src/Polly.Core/Timeout/TimeoutUtil.cs b/src/Polly.Core/Timeout/TimeoutUtil.cs index 79f0bc597be..6a47d724227 100644 --- a/src/Polly.Core/Timeout/TimeoutUtil.cs +++ b/src/Polly.Core/Timeout/TimeoutUtil.cs @@ -8,14 +8,4 @@ public static bool ShouldApplyTimeout(TimeSpan timeout) { return timeout > TimeSpan.Zero; } - - public static bool IsTimeoutValid(TimeSpan timeout) - { - if (timeout > TimeSpan.Zero) - { - return true; - } - - return timeout == System.Threading.Timeout.InfiniteTimeSpan || timeout > TimeSpan.Zero; - } } diff --git a/src/Polly.Core/Utils/TimeSpanAttribute.cs b/src/Polly.Core/Utils/TimeSpanAttribute.cs deleted file mode 100644 index e5f4e8589d0..00000000000 --- a/src/Polly.Core/Utils/TimeSpanAttribute.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Globalization; - -#pragma warning disable CA1019 // Define accessors for attribute arguments - -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] -internal sealed class TimeSpanAttribute : ValidationAttribute -{ - public TimeSpan Minimum => TimeSpan.Parse(_min, CultureInfo.InvariantCulture); - - public TimeSpan? Maximum => _max == null ? null : TimeSpan.Parse(_max, CultureInfo.InvariantCulture); - - private readonly string _min; - private readonly string? _max; - - public TimeSpanAttribute(string min) - { - _min = min; - _max = null; - } - - public TimeSpanAttribute(string min, string max) - { - _min = min; - _max = max; - } - - protected override ValidationResult IsValid(object? value, ValidationContext? validationContext) - { - var min = Minimum; - var max = Maximum; - - if (value is TimeSpan ts) - { - if (ts < min) - { - return new ValidationResult($"The field {validationContext.GetDisplayName()} must be >= to {min}.", validationContext.GetMemberName()); - } - - if (max.HasValue) - { - if (ts > max.Value) - { - return new ValidationResult($"The field {validationContext.GetDisplayName()} must be <= to {max}.", validationContext.GetMemberName()); - } - } - } - - return ValidationResult.Success!; - } -} diff --git a/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs index f2a15ea5155..8a8a15fb847 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs @@ -91,8 +91,8 @@ public void InvalidOptions_Validate() Validation Errors: The field MinimumThroughput must be between 2 and 2147483647. - The field SamplingDuration must be >= to 00:00:00.5000000. - The field BreakDuration must be >= to 00:00:00.5000000. + The field SamplingDuration must be between 00:00:00.5000000 and 1.00:00:00. + The field BreakDuration must be between 00:00:00.5000000 and 1.00:00:00. The ShouldHandle field is required. """); } diff --git a/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs index 7eaac28b43c..3b22e052ecc 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs @@ -83,7 +83,7 @@ public void InvalidOptions_Validate() Validation Errors: The field FailureThreshold must be between 1 and 2147483647. - The field BreakDuration must be >= to 00:00:00.5000000. + The field BreakDuration must be between 00:00:00.5000000 and 1.00:00:00. The ShouldHandle field is required. """); } diff --git a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs index 73d460efbad..1b482313187 100644 --- a/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs +++ b/test/Polly.Core.Tests/Retry/RetryStrategyOptionsTests.cs @@ -55,7 +55,7 @@ Invalid Options Validation Errors: The field RetryCount must be between -1 and 100. - The field BaseDelay must be >= to 00:00:00. + The field BaseDelay must be between 00:00:00 and 1.00:00:00. The ShouldHandle field is required. """); } diff --git a/test/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs deleted file mode 100644 index cee38631de1..00000000000 --- a/test/Polly.Core.Tests/Timeout/TimeoutAttributeTests.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Polly.Timeout; - -namespace Polly.Core.Tests.Timeout; - -public class TimeoutAttributeTests -{ - [Fact] - public void IsValid_Object_True() - { - new TimeoutAttribute().IsValid(new object()).Should().BeTrue(); - } -} diff --git a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs index 10e828e2904..e6dacefe222 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyBuilderExtensionsTests.cs @@ -56,11 +56,11 @@ public void AddTimeout_InvalidOptions_Throws() private static TimeSpan GetTimeout(TimeoutResilienceStrategy strategy) { - if (strategy.TimeoutGenerator == null) + if (strategy.TimeoutGenerator is null) { return strategy.DefaultTimeout; } - return strategy.GenerateTimeoutAsync(ResilienceContext.Get()).Preserve().GetAwaiter().GetResult(); + return strategy.TimeoutGenerator(new TimeoutGeneratorArguments(ResilienceContext.Get())).Preserve().GetAwaiter().GetResult(); } } diff --git a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs index 8816528cb0d..9d547ccf240 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutResilienceStrategyTests.cs @@ -79,8 +79,8 @@ public async Task Execute_EnsureOnTimeoutCalled() public void Execute_NoTimeout(TimeSpan timeout) { var called = false; - var sut = CreateSut(); SetTimeout(timeout); + var sut = CreateSut(); sut.Execute(_ => { }); called.Should().BeFalse(); diff --git a/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs b/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs index 33b271535bc..8295700e6be 100644 --- a/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs +++ b/test/Polly.Core.Tests/Timeout/TimeoutTestUtils.cs @@ -12,12 +12,13 @@ public static class TimeoutTestUtils { TimeSpan.MinValue, TimeSpan.Zero, - TimeSpan.FromSeconds(-1) + TimeSpan.FromSeconds(-1), + TimeSpan.FromHours(25), }; public static readonly TheoryData ValidTimeouts = new() { - System.Threading.Timeout.InfiniteTimeSpan, - TimeSpan.FromSeconds(1) + TimeSpan.FromSeconds(1), + TimeSpan.FromHours(1), }; } diff --git a/test/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs b/test/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs deleted file mode 100644 index c421cf9b33a..00000000000 --- a/test/Polly.Core.Tests/Utils/TimeSpanAttributeTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Polly.Core.Tests.Utils; - -public class TimeSpanAttributeTests -{ - [Fact] - public void InvalidValue_Skipped() - { - var attr = new TimeSpanAttribute("00:00:01"); - - attr.GetValidationResult(new object(), new ValidationContext(TimeSpan.FromSeconds(1)) { DisplayName = "A" }) - .Should().Be(ValidationResult.Success); - } - - [Fact] - public void InvalidMinValue_Validated() - { - var attr = new TimeSpanAttribute("00:00:01"); - - attr.GetValidationResult(TimeSpan.FromSeconds(0), new ValidationContext(TimeSpan.FromSeconds(0)) { DisplayName = "A" })! - .ErrorMessage.Should().Be("The field A must be >= to 00:00:01."); - - attr.GetValidationResult(TimeSpan.FromSeconds(1), new ValidationContext(TimeSpan.FromSeconds(1)) { DisplayName = "A" }) - .Should().Be(ValidationResult.Success); - } - - [Fact] - public void InvalidMaxValue_Validated() - { - var attr = new TimeSpanAttribute("00:00:01", "00:00:03"); - - attr - .GetValidationResult(TimeSpan.FromSeconds(0), new ValidationContext(TimeSpan.FromSeconds(0)) { DisplayName = "A" })! - .ErrorMessage.Should().Be("The field A must be >= to 00:00:01."); - attr.GetValidationResult(TimeSpan.FromSeconds(1), new ValidationContext(TimeSpan.FromSeconds(1)) { DisplayName = "A" }) - .Should().Be(ValidationResult.Success); - - attr - .GetValidationResult(TimeSpan.FromSeconds(4), new ValidationContext(TimeSpan.FromSeconds(4)) { DisplayName = "A" })! - .ErrorMessage.Should().Be("The field A must be <= to 00:00:03."); - attr.GetValidationResult(TimeSpan.FromSeconds(3), new ValidationContext(TimeSpan.FromSeconds(3)) { DisplayName = "A" }) - .Should().Be(ValidationResult.Success); - } -}