-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Introduce FaultGenerator and OutcomeGenerator<T>
#1911
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| using System.ComponentModel; | ||
| using Polly.Simmy.Utils; | ||
|
|
||
| namespace Polly.Simmy.Fault; | ||
|
|
||
| #pragma warning disable CA2225 // Operator overloads have named alternates | ||
| #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters | ||
|
|
||
| /// <summary> | ||
| /// A generator for creating faults (exceptions) using registered delegate functions. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// An instance of this class can be assigned to the <see cref="FaultStrategyOptions.FaultGenerator"/> property. | ||
| /// </remarks> | ||
| public class FaultGenerator | ||
| { | ||
| private const int DefaultWeight = 100; | ||
|
|
||
| private readonly GeneratorHelper<VoidResult> _helper; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="FaultGenerator"/> class. | ||
| /// </summary> | ||
| public FaultGenerator() | ||
| => _helper = new GeneratorHelper<VoidResult>(RandomUtil.Instance.Next); | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator delegate. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the exception.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
| public FaultGenerator AddException(Func<Exception> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(_ => Outcome.FromException<VoidResult>(generator()), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator delegate that accepts a <see cref="ResilienceContext"/>. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the exception, accepting a <see cref="ResilienceContext"/>.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
| public FaultGenerator AddException(Func<ResilienceContext, Exception> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(context => Outcome.FromException<VoidResult>(generator(context)), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator for a specific exception type, using the default constructor of that exception. | ||
| /// </summary> | ||
| /// <typeparam name="TException">The type of the exception to generate.</typeparam> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="FaultGenerator"/>.</returns> | ||
| public FaultGenerator AddException<TException>(int weight = DefaultWeight) | ||
| where TException : Exception, new() | ||
| { | ||
| _helper.AddOutcome(_ => Outcome.FromException<VoidResult>(new TException()), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Provides an implicit conversion from <see cref="FaultGenerator"/> to a delegate compatible with <see cref="FaultStrategyOptions.FaultGenerator"/>. | ||
| /// </summary> | ||
| /// <param name="generator">The instance of <see cref="FaultGenerator"/>.</param> | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public static implicit operator Func<FaultGeneratorArguments, ValueTask<Exception?>>(FaultGenerator generator) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| var generatorDelegate = generator._helper.CreateGenerator(); | ||
|
|
||
| return args => new ValueTask<Exception?>(generatorDelegate(args.Context)!.Value.Exception); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| using System.ComponentModel; | ||
| using Polly.Simmy.Utils; | ||
|
|
||
| namespace Polly.Simmy.Outcomes; | ||
|
|
||
| #pragma warning disable CA2225 // Operator overloads have named alternates | ||
| #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters | ||
|
|
||
| /// <summary> | ||
| /// Generator that produces faults such as exceptions or results. | ||
| /// </summary> | ||
| /// <typeparam name="TResult">The type of the result.</typeparam> | ||
| /// <remarks> | ||
| /// Instance of this class is assignable to <see cref="OutcomeStrategyOptions{TResult}.OutcomeGenerator"/>. | ||
| /// </remarks> | ||
| public class OutcomeGenerator<TResult> | ||
| { | ||
| private const int DefaultWeight = 100; | ||
| private readonly GeneratorHelper<TResult> _helper; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="OutcomeGenerator{TResult}"/> class. | ||
| /// </summary> | ||
| public OutcomeGenerator() | ||
| : this(RandomUtil.Instance.Next) | ||
| { | ||
| } | ||
|
|
||
| internal OutcomeGenerator(Func<int, int> weightGenerator) | ||
| => _helper = new GeneratorHelper<TResult>(weightGenerator); | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator delegate. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the exception.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
| public OutcomeGenerator<TResult> AddException(Func<Exception> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(_ => Outcome.FromException<TResult>(generator()), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator delegate that accepts a <see cref="ResilienceContext"/>. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the exception, accepting a <see cref="ResilienceContext"/>.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
| public OutcomeGenerator<TResult> AddException(Func<ResilienceContext, Exception> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(context => Outcome.FromException<TResult>(generator(context)), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers an exception generator for a specific exception type, using the default constructor of that exception. | ||
| /// </summary> | ||
| /// <typeparam name="TException">The type of the exception to generate.</typeparam> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
| public OutcomeGenerator<TResult> AddException<TException>(int weight = DefaultWeight) | ||
| where TException : Exception, new() | ||
| { | ||
| _helper.AddOutcome(_ => Outcome.FromException<TResult>(new TException()), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers a result generator. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the result.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
| public OutcomeGenerator<TResult> AddResult(Func<TResult> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(_ => Outcome.FromResult(generator()), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Registers a result generator. | ||
| /// </summary> | ||
| /// <param name="generator">The delegate that generates the result, accepting a <see cref="ResilienceContext"/>.</param> | ||
| /// <param name="weight">The weight assigned to this generator. Defaults to <c>100</c>.</param> | ||
| /// <returns>The current instance of <see cref="OutcomeGenerator{TResult}"/>.</returns> | ||
| public OutcomeGenerator<TResult> AddResult(Func<ResilienceContext, TResult> generator, int weight = DefaultWeight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _helper.AddOutcome(context => Outcome.FromResult(generator(context)), weight); | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Implicit conversion to <see cref="OutcomeStrategyOptions{TResult}.OutcomeGenerator"/>. | ||
| /// </summary> | ||
| /// <param name="generator">The generator instance.</param> | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public static implicit operator Func<OutcomeGeneratorArguments, ValueTask<Outcome<TResult>?>>(OutcomeGenerator<TResult> generator) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| var generatorDelegate = generator._helper.CreateGenerator(); | ||
|
|
||
| return args => new ValueTask<Outcome<TResult>?>(generatorDelegate(args.Context)); | ||
| } | ||
| } | ||
|
|
||
24 changes: 0 additions & 24 deletions
24
src/Polly.Core/Simmy/Outcomes/OutcomeStrategyOptions.TResult.cs
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,24 @@ | ||
| namespace Polly.Simmy.Outcomes; | ||
| using System.ComponentModel.DataAnnotations; | ||
|
|
||
| /// <inheritdoc/> | ||
| public class OutcomeStrategyOptions : OutcomeStrategyOptions<object> | ||
| namespace Polly.Simmy.Outcomes; | ||
|
|
||
| /// <summary> | ||
| /// Represents the options for the Outcome chaos strategy. | ||
| /// </summary> | ||
| /// <typeparam name="TResult">The type of the outcome that was injected.</typeparam> | ||
| public class OutcomeStrategyOptions<TResult> : MonkeyStrategyOptions | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets the delegate that's raised when the outcome is injected. | ||
martintmk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// </summary> | ||
| /// <remarks> | ||
| /// Defaults to <see langword="null"/>. | ||
| /// </remarks> | ||
| public Func<OnOutcomeInjectedArguments<TResult>, ValueTask>? OnOutcomeInjected { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the outcome generator to be injected for a given execution. | ||
| /// </summary> | ||
| [Required] | ||
| public Func<OutcomeGeneratorArguments, ValueTask<Outcome<TResult>?>> OutcomeGenerator { get; set; } = default!; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| namespace Polly.Simmy.Utils; | ||
|
|
||
| internal class GeneratorHelper<TResult> | ||
martintmk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| private readonly Func<int, int> _weightGenerator; | ||
|
|
||
| private readonly List<int> _weights = []; | ||
| private readonly List<Func<ResilienceContext, Outcome<TResult>>> _factories = []; | ||
| private int _totalWeight; | ||
|
|
||
| public GeneratorHelper(Func<int, int> weightGenerator) => _weightGenerator = weightGenerator; | ||
|
|
||
| public void AddOutcome(Func<ResilienceContext, Outcome<TResult>> generator, int weight) | ||
| { | ||
| Guard.NotNull(generator); | ||
|
|
||
| _totalWeight += weight; | ||
| _factories.Add(generator); | ||
| _weights.Add(weight); | ||
| } | ||
|
|
||
| internal Func<ResilienceContext, Outcome<TResult>?> CreateGenerator() | ||
| { | ||
| if (_factories.Count == 0) | ||
| { | ||
| return _ => null; | ||
| } | ||
|
|
||
| var totalWeight = _totalWeight; | ||
| var factories = _factories.ToArray(); | ||
| var weights = _weights.ToArray(); | ||
| var generator = _weightGenerator; | ||
|
|
||
| return context => | ||
| { | ||
| var generatedWeight = generator(totalWeight); | ||
| var weight = 0; | ||
|
|
||
| for (var i = 0; i < factories.Length; i++) | ||
| { | ||
| weight += weights[i]; | ||
| if (generatedWeight < weight) | ||
| { | ||
| return factories[i](context); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| }; | ||
| } | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.