elsa-core/src/modules/Elsa.Resilience/Features/ResilienceFeature.cs
Sipke Schoorstra 58719078d0
[codex] Secure Resilience simulate response endpoint (#7505)
* Secure simulate response endpoint

* Address simulate response review feedback

* Clamp simulate response session index

* Isolate simulate response security tests

* Address simulate response review feedback

* Address simulate response review feedback
2026-05-22 15:27:13 +02:00

99 lines
4.1 KiB
C#

using Elsa.Expressions.Options;
using Elsa.Extensions;
using Elsa.Features.Abstractions;
using Elsa.Features.Services;
using Elsa.Resilience.Endpoints.SimulateResponse;
using Elsa.Resilience.Entities;
using Elsa.Resilience.Modifiers;
using Elsa.Resilience.Options;
using Elsa.Resilience.Recorders;
using Elsa.Resilience.Serialization;
using Elsa.Resilience.StrategySources;
using Elsa.Workflows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Elsa.Resilience.Features;
public class ResilienceFeature(IModule module) : FeatureBase(module)
{
private Func<IServiceProvider, IRetryAttemptRecorder> _retryAttemptRecorder = sp => sp.GetRequiredService<ActivityExecutionContextRetryAttemptRecorder>();
private Func<IServiceProvider, IRetryAttemptReader> _retryAttemptReader = sp => sp.GetRequiredService<ActivityExecutionContextRetryAttemptReader>();
public ResilienceFeature AddResilienceStrategyType<T>() where T : IResilienceStrategy
{
return AddResilienceStrategyType(typeof(T));
}
public ResilienceFeature AddResilienceStrategyType(Type strategyType)
{
Services.Configure<ResilienceOptions>(options => options.StrategyTypes.Add(strategyType));
return this;
}
public ResilienceFeature WithActivityExecutionContextRetryAttemptRecorder()
{
return WithRetryAttemptRecorder<ActivityExecutionContextRetryAttemptRecorder>()
.WithRetryAttemptReader<ActivityExecutionContextRetryAttemptReader>();
}
public ResilienceFeature WithVoidRetryAttemptRecorder()
{
return WithRetryAttemptRecorder<VoidRetryAttemptRecorder>()
.WithRetryAttemptReader<VoidRetryAttemptReader>();
}
public ResilienceFeature WithRetryAttemptRecorder<T>() => WithRetryAttemptRecorder(sp => (IRetryAttemptRecorder)ActivatorUtilities.CreateInstance<T>(sp)!);
public ResilienceFeature WithRetryAttemptReader<T>() => WithRetryAttemptReader(sp => (IRetryAttemptReader)ActivatorUtilities.CreateInstance<T>(sp)!);
public ResilienceFeature WithRetryAttemptRecorder(Func<IServiceProvider, IRetryAttemptRecorder> recorder)
{
_retryAttemptRecorder = recorder;
return this;
}
public ResilienceFeature WithRetryAttemptReader(Func<IServiceProvider, IRetryAttemptReader> reader)
{
_retryAttemptReader = reader;
return this;
}
public override void Configure()
{
Module.AddFastEndpointsAssembly<ResilienceFeature>();
Services.Configure<ExpressionOptions>(options =>
{
options.AddTypeAlias<List<RetryAttemptRecord>>("RetryAttemptRecordList");
});
}
public override void Apply()
{
Services.AddOptions<ResilienceOptions>();
Services.AddOptions<SimulateResponseOptions>();
Services.TryAddSingleton(TimeProvider.System);
Services
.AddSingleton<ResilienceStrategySerializer>()
.AddSingleton<SimulateResponseSessionStore>()
.AddSingleton<IActivityDescriptorModifier, ResilientActivityDescriptorModifier>()
.AddScoped<IResilienceStrategyCatalog, ResilienceStrategyCatalog>()
.AddScoped<IResilienceStrategyConfigEvaluator, ResilienceStrategyConfigEvaluator>()
.AddScoped<IResilientActivityInvoker, ResilientActivityInvoker>()
.AddScoped<IResilienceStrategySource, ConfigurationResilienceStrategySource>()
.AddSingleton(VoidRetryAttemptRecorder.Instance)
.AddSingleton(VoidRetryAttemptReader.Instance)
.AddScoped<ActivityExecutionContextRetryAttemptRecorder>()
.AddScoped<ActivityExecutionContextRetryAttemptReader>()
.AddScoped(_retryAttemptRecorder)
.AddScoped(_retryAttemptReader)
.AddHandlersFrom<ResilienceFeature>();
// Register transient exception detection infrastructure
Services
.AddSingleton<ITransientExceptionStrategy, DefaultTransientExceptionStrategy>()
.AddSingleton<ITransientExceptionDetector, TransientExceptionDetector>();
}
}