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)
};
}
}