using System.Text.Json.Serialization; namespace DysonNetwork.Shared.Error; /// /// Standardized error payload to return to clients. /// Inspired by RFC7807 (problem+json) with app-specific fields. /// public class ApiError { /// /// Application-specific error code (e.g., "VALIDATION_ERROR", "NOT_FOUND", "SERVER_ERROR"). /// [JsonPropertyName("code")] public string Code { get; set; } = "UNKNOWN_ERROR"; /// /// Short, human-readable message for the error. /// [JsonPropertyName("message")] public string Message { get; set; } = "An unexpected error occurred."; /// /// HTTP status code to be used by the server when sending this error. /// Optional to keep the model transport-agnostic. /// [JsonPropertyName("status")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Status { get; set; } /// /// More detailed description of the error. /// [JsonPropertyName("detail")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; set; } /// /// Server trace identifier (e.g., from HttpContext.TraceIdentifier) to help debugging. /// [JsonPropertyName("traceId")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TraceId { get; set; } /// /// Field-level validation errors: key is the field name, value is an array of messages. /// [JsonPropertyName("errors")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary? Errors { get; set; } /// /// Arbitrary additional metadata for clients. /// [JsonPropertyName("meta")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary? Meta { get; set; } /// /// Factory for a validation error payload. /// public static ApiError Validation( Dictionary errors, string? message = null, int status = 400, string code = "VALIDATION_ERROR", string? traceId = null) { return new ApiError { Code = code, Message = message ?? "One or more validation errors occurred.", Status = status, Errors = errors, TraceId = traceId }; } /// /// Factory for a not-found error payload. /// public static ApiError NotFound( string resource, string? message = null, int status = 404, string code = "NOT_FOUND", string? traceId = null) { return new ApiError { Code = code, Message = message ?? $"The requested resource '{resource}' was not found.", Status = status, Detail = resource, TraceId = traceId }; } /// /// Factory for a generic server error payload. /// public static ApiError Server( string? message = null, int status = 500, string code = "SERVER_ERROR", string? traceId = null, string? detail = null) { return new ApiError { Code = code, Message = message ?? "An internal server error occurred.", Status = status, TraceId = traceId, Detail = detail }; } /// /// Factory for an unauthorized/forbidden error payload. /// public static ApiError Unauthorized( string? message = null, bool forbidden = false, string? traceId = null) { return new ApiError { Code = forbidden ? "FORBIDDEN" : "UNAUTHORIZED", Message = message ?? (forbidden ? "You do not have permission to perform this action." : "Authentication is required."), Status = forbidden ? 403 : 401, TraceId = traceId }; } }