Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace StellaOps.Router.Gateway.OpenApi;
|
||||
|
||||
/// <summary>
|
||||
/// Endpoints for serving OpenAPI documentation.
|
||||
/// </summary>
|
||||
public static class OpenApiEndpoints
|
||||
{
|
||||
private static readonly ISerializer YamlSerializer = new SerializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// Maps OpenAPI endpoints to the application.
|
||||
/// </summary>
|
||||
public static IEndpointRouteBuilder MapRouterOpenApiEndpoints(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapGet("/.well-known/openapi", GetOpenApiDiscovery)
|
||||
.ExcludeFromDescription();
|
||||
|
||||
endpoints.MapGet("/openapi.json", GetOpenApiJson)
|
||||
.ExcludeFromDescription();
|
||||
|
||||
endpoints.MapGet("/openapi.yaml", GetOpenApiYaml)
|
||||
.ExcludeFromDescription();
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
private static IResult GetOpenApiDiscovery(
|
||||
[FromServices] IRouterOpenApiDocumentCache cache,
|
||||
HttpContext context)
|
||||
{
|
||||
var (_, etag, generatedAt) = cache.GetDocument();
|
||||
|
||||
var discovery = new
|
||||
{
|
||||
openapi_json = "/openapi.json",
|
||||
openapi_yaml = "/openapi.yaml",
|
||||
etag,
|
||||
generated_at = generatedAt.ToString("O")
|
||||
};
|
||||
|
||||
context.Response.Headers.CacheControl = "public, max-age=60";
|
||||
return Results.Ok(discovery);
|
||||
}
|
||||
|
||||
private static IResult GetOpenApiJson(
|
||||
[FromServices] IRouterOpenApiDocumentCache cache,
|
||||
HttpContext context)
|
||||
{
|
||||
var (documentJson, etag, _) = cache.GetDocument();
|
||||
|
||||
// Check If-None-Match header
|
||||
if (context.Request.Headers.TryGetValue("If-None-Match", out var ifNoneMatch))
|
||||
{
|
||||
if (ifNoneMatch == etag)
|
||||
{
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=60";
|
||||
return Results.StatusCode(304);
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=60";
|
||||
return Results.Content(documentJson, "application/json; charset=utf-8");
|
||||
}
|
||||
|
||||
private static IResult GetOpenApiYaml(
|
||||
[FromServices] IRouterOpenApiDocumentCache cache,
|
||||
HttpContext context)
|
||||
{
|
||||
var (documentJson, etag, _) = cache.GetDocument();
|
||||
|
||||
// Check If-None-Match header
|
||||
if (context.Request.Headers.TryGetValue("If-None-Match", out var ifNoneMatch))
|
||||
{
|
||||
if (ifNoneMatch == etag)
|
||||
{
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=60";
|
||||
return Results.StatusCode(304);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert JSON to YAML
|
||||
var jsonNode = JsonNode.Parse(documentJson);
|
||||
var yamlContent = ConvertToYaml(jsonNode);
|
||||
|
||||
context.Response.Headers.ETag = etag;
|
||||
context.Response.Headers.CacheControl = "public, max-age=60";
|
||||
return Results.Content(yamlContent, "application/yaml; charset=utf-8");
|
||||
}
|
||||
|
||||
private static string ConvertToYaml(JsonNode? node)
|
||||
{
|
||||
if (node is null)
|
||||
return string.Empty;
|
||||
|
||||
var obj = ConvertJsonNodeToObject(node);
|
||||
return YamlSerializer.Serialize(obj);
|
||||
}
|
||||
|
||||
private static object? ConvertJsonNodeToObject(JsonNode? node)
|
||||
{
|
||||
return node switch
|
||||
{
|
||||
null => null,
|
||||
JsonObject obj => obj.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => ConvertJsonNodeToObject(kvp.Value)),
|
||||
JsonArray arr => arr.Select(ConvertJsonNodeToObject).ToList(),
|
||||
JsonValue val => val.GetValue<object>(),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user