From fdd0c85a9243e570cd613c603e43723ec8ccd3cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 07:24:17 +0000 Subject: [PATCH 1/2] Initial plan From 521ebddbc012780bacb931f86250c7443ec2aac8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 07:33:28 +0000 Subject: [PATCH 2/2] Fix compilation errors in Proxy project to enable testing Co-authored-by: visionarycoder <8689814+visionarycoder@users.noreply.github.com> --- ...yInterceptorServiceCollectionExtensions.cs | 35 +++++++++++++++++++ .../Resilience/ResilienceInterceptor.cs | 13 +++++++ .../Security/SecurityInterceptor.cs | 6 ++++ ...yInterceptorServiceCollectionExtensions.cs | 22 ++++++++++++ 4 files changed, 76 insertions(+) diff --git a/src/VisionaryCoder.Framework.Proxy/Interceptors/ProxyInterceptorServiceCollectionExtensions.cs b/src/VisionaryCoder.Framework.Proxy/Interceptors/ProxyInterceptorServiceCollectionExtensions.cs index cedf6db..b4131b3 100644 --- a/src/VisionaryCoder.Framework.Proxy/Interceptors/ProxyInterceptorServiceCollectionExtensions.cs +++ b/src/VisionaryCoder.Framework.Proxy/Interceptors/ProxyInterceptorServiceCollectionExtensions.cs @@ -77,35 +77,70 @@ public static IServiceCollection AddJwtBearerEnricher( }); /// Adds the telemetry interceptor (order -50). public static IServiceCollection AddTelemetryInterceptor(this IServiceCollection services) + { services.TryAddSingleton(new ActivitySource("VisionaryCoder.Framework.Proxy")); services.TryAddTransient(); + return services; + } + /// Adds the correlation interceptor (order 0). public static IServiceCollection AddCorrelationInterceptor(this IServiceCollection services) + { services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddTransient(); + return services; + } + /// Adds the logging interceptor (order 100). public static IServiceCollection AddLoggingInterceptor(this IServiceCollection services) + { services.TryAddTransient(); + return services; + } + /// Adds the caching interceptor (order 150). public static IServiceCollection AddCachingInterceptor(this IServiceCollection services) + { services.TryAddTransient(); + return services; + } + /// Adds a proxy cache implementation. public static IServiceCollection AddProxyCache(this IServiceCollection services) where TCache : class, IProxyCache + { services.TryAddSingleton(); + return services; + } + /// Adds the resilience interceptor (order 180). public static IServiceCollection AddResilienceInterceptor(this IServiceCollection services) + { services.TryAddTransient(); + return services; + } + /// Adds the retry interceptor (order 200). public static IServiceCollection AddRetryInterceptor(this IServiceCollection services) + { services.TryAddTransient(); + return services; + } + /// Adds the auditing interceptor (order 300). public static IServiceCollection AddAuditingInterceptor(this IServiceCollection services) + { services.TryAddTransient(); services.TryAddTransient(); + return services; + } + /// Adds an audit sink. public static IServiceCollection AddAuditSink(this IServiceCollection services) where TSink : class, IAuditSink + { services.TryAddTransient(); + return services; + } } diff --git a/src/VisionaryCoder.Framework.Proxy/Interceptors/Resilience/ResilienceInterceptor.cs b/src/VisionaryCoder.Framework.Proxy/Interceptors/Resilience/ResilienceInterceptor.cs index df55c6c..edd55d6 100644 --- a/src/VisionaryCoder.Framework.Proxy/Interceptors/Resilience/ResilienceInterceptor.cs +++ b/src/VisionaryCoder.Framework.Proxy/Interceptors/Resilience/ResilienceInterceptor.cs @@ -24,13 +24,16 @@ public ResilienceInterceptor(ILogger logger, ResiliencePi this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); this.resiliencePipeline = resiliencePipeline ?? CreateDefaultPipeline(); } + /// /// Invokes the interceptor with resilience protection. + /// /// The type of the response data. /// The proxy context. /// The next delegate in the pipeline. /// The cancellation token to monitor for cancellation requests. /// A task representing the asynchronous operation with the response. public async Task> InvokeAsync(ProxyContext context, ProxyDelegate next, CancellationToken cancellationToken = default) + { var operationName = context.OperationName ?? "Unknown"; var correlationId = context.CorrelationId ?? "Undefined"; try @@ -42,12 +45,19 @@ public async Task> InvokeAsync(ProxyContext context, ProxyDelegat return response; } catch (Exception ex) + { context.Metadata["ResilienceException"] = ex.GetType().Name; logger.LogError(ex, "Resilience pipeline failed for operation '{OperationName}'. Correlation ID: '{CorrelationId}'", operationName, correlationId); throw; + } + } + + /// /// Creates a default resilience pipeline with retry and circuit breaker. + /// /// A configured resilience pipeline. private static ResiliencePipeline CreateDefaultPipeline() + { return new ResiliencePipelineBuilder() .AddRetry(new() { @@ -57,10 +67,13 @@ private static ResiliencePipeline CreateDefaultPipeline() UseJitter = true }) .AddCircuitBreaker(new() + { FailureRatio = 0.5, SamplingDuration = TimeSpan.FromSeconds(30), MinimumThroughput = 5, BreakDuration = TimeSpan.FromMinutes(1) + }) .AddTimeout(TimeSpan.FromSeconds(30)) .Build(); + } } diff --git a/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptor.cs b/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptor.cs index 2b6ec53..64f22af 100644 --- a/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptor.cs +++ b/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptor.cs @@ -34,6 +34,7 @@ public async Task> InvokeAsync( ProxyContext context, ProxyDelegate next, CancellationToken cancellationToken = default) + { using var _ = logger.BeginScope("SecurityInterceptor for {RequestType}", context.Request?.GetType().Name ?? "Unknown"); try @@ -45,15 +46,20 @@ public async Task> InvokeAsync( } // Check authorization policies foreach (var policy in policies) + { if (!await policy.IsAuthorizedAsync(context, cancellationToken)) { logger.LogWarning("Authorization failed for policy {PolicyType}", policy.GetType().Name); return Response.Failure("Authorization failed"); } + } logger.LogDebug("Security validation passed, proceeding to next interceptor"); return await next(context, cancellationToken); } catch (Exception ex) when (ex is not ProxyException) + { logger.LogError(ex, "Unexpected error during security processing"); return Response.Failure("Security processing failed"); + } + } } diff --git a/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptorServiceCollectionExtensions.cs b/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptorServiceCollectionExtensions.cs index b63e12e..af965a6 100644 --- a/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptorServiceCollectionExtensions.cs +++ b/src/VisionaryCoder.Framework.Proxy/Interceptors/Security/SecurityInterceptorServiceCollectionExtensions.cs @@ -26,17 +26,39 @@ public static IServiceCollection AddJwtBearerInterceptor( }); return services; } + /// /// Adds the JWT Bearer interceptor that retrieves tokens from a secret provider. + /// + /// The service collection to add the interceptor to. /// The name of the secret containing the JWT token. + /// The service collection for chaining. + public static IServiceCollection AddJwtBearerInterceptorFromSecret( + this IServiceCollection services, string secretName) + { + services.AddSingleton(provider => + { var secretProvider = provider.GetRequiredService(); + var logger = provider.GetRequiredService>(); Func> tokenProvider = async (cancellationToken) => { return await secretProvider.GetAsync(secretName, cancellationToken); }; + return new JwtBearerInterceptor(logger, tokenProvider); + }); + return services; + } + + /// /// Adds the JWT Bearer interceptor with a static token (useful for development). + /// + /// The service collection to add the interceptor to. /// The static JWT token to use. + /// The service collection for chaining. public static IServiceCollection AddJwtBearerInterceptorWithStaticToken( + this IServiceCollection services, string staticToken) + { return services.AddJwtBearerInterceptor((cancellationToken) => Task.FromResult(staticToken)); + } }