using System.Text.Json; using System.Text.Json.Serialization; namespace StellaOps.Microservice.Validation; /// /// RFC 7807 Problem Details for schema validation failures. /// Returns HTTP 422 Unprocessable Entity. /// public sealed class ValidationProblemDetails { private static readonly JsonSerializerOptions SerializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; /// /// A URI reference identifying the problem type. /// [JsonPropertyName("type")] public string Type { get; init; } = "https://stellaops.io/errors/schema-validation"; /// /// A short, human-readable summary of the problem. /// [JsonPropertyName("title")] public string Title { get; init; } = "Schema Validation Failed"; /// /// The HTTP status code (422). /// [JsonPropertyName("status")] public int Status { get; init; } = 422; /// /// A human-readable explanation specific to this occurrence. /// [JsonPropertyName("detail")] public string? Detail { get; init; } /// /// A URI reference identifying the specific occurrence (the endpoint path). /// [JsonPropertyName("instance")] public string? Instance { get; init; } /// /// The trace/correlation ID for distributed tracing. /// [JsonPropertyName("traceId")] public string? TraceId { get; init; } /// /// The list of validation errors. /// [JsonPropertyName("errors")] public IReadOnlyList Errors { get; init; } = []; /// /// Creates a ValidationProblemDetails for schema validation failures. /// /// The HTTP method. /// The endpoint path. /// Request or response validation. /// The validation errors. /// Optional correlation ID. public static ValidationProblemDetails Create( string method, string path, SchemaDirection direction, IReadOnlyList errors, string? correlationId = null) { var directionText = direction == SchemaDirection.Request ? "request" : "response"; return new ValidationProblemDetails { Detail = $"{char.ToUpperInvariant(directionText[0])}{directionText[1..]} body failed schema validation for {method} {path}", Instance = path, TraceId = correlationId, Errors = errors }; } /// /// Converts this problem details to a RawResponse. /// public RawResponse ToRawResponse() { var json = JsonSerializer.SerializeToUtf8Bytes(this, SerializerOptions); var headers = new HeaderCollection(); headers.Set("Content-Type", "application/problem+json; charset=utf-8"); return new RawResponse { StatusCode = Status, Headers = headers, Body = new MemoryStream(json) }; } }