using System.Text; using System.Text.Json; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using StellaOps.Policy.Engine.Options; using StellaOps.Policy.Engine.Streaming; using StellaOps.Policy.Engine.Overlay; namespace StellaOps.Policy.Engine.Endpoints; public static class PathScopeSimulationEndpoint { public static IEndpointRouteBuilder MapPathScopeSimulation(this IEndpointRouteBuilder routes) { routes.MapPost("/simulation/path-scope", HandleAsync) .RequireRateLimiting(PolicyEngineRateLimitOptions.PolicyName) .WithName("PolicyEngine.PathScopeSimulation"); return routes; } private static async Task HandleAsync( [FromBody] PathScopeSimulationRequest request, PathScopeSimulationService service, PathScopeSimulationBridgeService bridge, CancellationToken cancellationToken) { try { var stream = service.StreamAsync(request, cancellationToken); var responseBuilder = new StringBuilder(); await foreach (var line in stream.ConfigureAwait(false)) { responseBuilder.AppendLine(line); } // Emit change event stub when run in what-if mode. if (request.Options.Deterministic && request.Options.IncludeTrace) { var bridgeRequest = new PathScopeSimulationBridgeRequest( Tenant: request.Tenant, Rules: Array.Empty(), Overlays: null, Paths: new[] { request }, Mode: "preview", Seed: null); await bridge.SimulateAsync(bridgeRequest, cancellationToken).ConfigureAwait(false); } return Results.Text(responseBuilder.ToString(), "application/x-ndjson", Encoding.UTF8); } catch (PathScopeSimulationException ex) { var errorLine = JsonSerializer.Serialize(ex.Error); return Results.Text(errorLine + "\n", "application/x-ndjson", Encoding.UTF8, StatusCodes.Status400BadRequest); } } }