Files
git.stella-ops.org/src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/Endpoints/StormBreakerEndpoints.cs
StellaOps Bot ef6e4b2067
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
oas-ci / oas-validate (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
sdk-generator-smoke / sdk-smoke (push) Has been cancelled
SDK Publish & Sign / sdk-publish (push) Has been cancelled
Merge branch 'main' of https://git.stella-ops.org/stella-ops.org/git.stella-ops.org
2025-11-27 21:45:32 +02:00

121 lines
4.3 KiB
C#

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using StellaOps.Notifier.Worker.StormBreaker;
namespace StellaOps.Notifier.WebService.Endpoints;
/// <summary>
/// REST API endpoints for storm breaker operations.
/// </summary>
public static class StormBreakerEndpoints
{
/// <summary>
/// Maps storm breaker API endpoints.
/// </summary>
public static RouteGroupBuilder MapStormBreakerEndpoints(this IEndpointRouteBuilder endpoints)
{
var group = endpoints.MapGroup("/api/v2/storm-breaker")
.WithTags("Storm Breaker")
.WithOpenApi();
// List active storms for tenant
group.MapGet("/storms", async (
HttpContext context,
IStormBreaker stormBreaker,
CancellationToken cancellationToken) =>
{
var tenantId = context.Request.Headers["X-Tenant-Id"].FirstOrDefault() ?? "default";
var storms = await stormBreaker.GetActiveStormsAsync(tenantId, cancellationToken);
return Results.Ok(new
{
tenantId,
activeStorms = storms.Select(s => new
{
s.TenantId,
s.StormKey,
s.StartedAt,
eventCount = s.EventIds.Count,
s.SuppressedCount,
s.LastActivityAt,
s.IsActive
}).ToList(),
count = storms.Count
});
})
.WithName("ListActiveStorms")
.WithSummary("Lists all active notification storms for a tenant");
// Get specific storm state
group.MapGet("/storms/{stormKey}", async (
string stormKey,
HttpContext context,
IStormBreaker stormBreaker,
CancellationToken cancellationToken) =>
{
var tenantId = context.Request.Headers["X-Tenant-Id"].FirstOrDefault() ?? "default";
var state = await stormBreaker.GetStateAsync(tenantId, stormKey, cancellationToken);
if (state is null)
{
return Results.NotFound(new { error = $"No storm found with key '{stormKey}'" });
}
return Results.Ok(new
{
state.TenantId,
state.StormKey,
state.StartedAt,
eventCount = state.EventIds.Count,
state.SuppressedCount,
state.LastActivityAt,
state.LastSummaryAt,
state.IsActive,
sampleEventIds = state.EventIds.Take(10).ToList()
});
})
.WithName("GetStormState")
.WithSummary("Gets the current state of a specific storm");
// Generate storm summary
group.MapPost("/storms/{stormKey}/summary", async (
string stormKey,
HttpContext context,
IStormBreaker stormBreaker,
CancellationToken cancellationToken) =>
{
var tenantId = context.Request.Headers["X-Tenant-Id"].FirstOrDefault() ?? "default";
var summary = await stormBreaker.GenerateSummaryAsync(tenantId, stormKey, cancellationToken);
if (summary is null)
{
return Results.NotFound(new { error = $"No storm found with key '{stormKey}'" });
}
return Results.Ok(summary);
})
.WithName("GenerateStormSummary")
.WithSummary("Generates a summary for an active storm");
// Clear storm state
group.MapDelete("/storms/{stormKey}", async (
string stormKey,
HttpContext context,
IStormBreaker stormBreaker,
CancellationToken cancellationToken) =>
{
var tenantId = context.Request.Headers["X-Tenant-Id"].FirstOrDefault() ?? "default";
await stormBreaker.ClearAsync(tenantId, stormKey, cancellationToken);
return Results.Ok(new { message = $"Storm '{stormKey}' cleared successfully" });
})
.WithName("ClearStorm")
.WithSummary("Clears a storm state manually");
return group;
}
}