using FluentValidation; using SatelliteProvider.Common.DTO; namespace SatelliteProvider.Api.Validators; // AZ-809: per-point validator invoked via RuleForEach on the parent // CreateRouteRequest. Each route waypoint must declare a valid WGS84 // coordinate; the parent validator checks min/max count of the points // collection separately. // // Error path: errors keys land at `points[i].lat` / `points[i].lon` per // FluentValidation's default child-property naming + GlobalValidatorConfig // camelCase normalization (matches the wire format set by // [JsonPropertyName("lat"|"lon")] on RoutePoint). public sealed class RoutePointValidator : AbstractValidator { private const double MinLat = -90.0; private const double MaxLat = 90.0; private const double MinLon = -180.0; private const double MaxLon = 180.0; public RoutePointValidator() { // `RoutePoint.Latitude` is the C# property name but the wire name is // `lat` via [JsonPropertyName]. OverridePropertyName chains AFTER the // first concrete rule (which provides the `TProperty` for the generic // extension) and aligns the FluentValidation error key with the wire // format — callers see `errors["points[i].lat"]` matching what they // posted rather than the camelCased C# name `latitude`. RuleFor(p => p.Latitude) .InclusiveBetween(MinLat, MaxLat) .WithMessage($"`lat` must be between {MinLat} and {MaxLat}.") .OverridePropertyName("lat"); RuleFor(p => p.Longitude) .InclusiveBetween(MinLon, MaxLon) .WithMessage($"`lon` must be between {MinLon} and {MaxLon}.") .OverridePropertyName("lon"); } }