using FluentValidation; using SatelliteProvider.Common.DTO; namespace SatelliteProvider.Api.Validators; // AZ-808: FluentValidation rules for POST /api/satellite/request. // Wired through ValidationEndpointFilter at endpoint // registration time (.WithValidation() in Program.cs). // Failures are converted to RFC 7807 ValidationProblemDetails per // _docs/02_document/contracts/api/error-shape.md v1.0.0. // // Required-field detection is handled at the deserializer level via // [JsonRequired] on RequestRegionRequest properties plus // JsonSerializerOptions.UnmappedMemberHandling.Disallow (AZ-795). This // validator covers the post-deserialization business rules: non-zero Id, // lat/lon/sizeMeters/zoomLevel range constraints. public sealed class RegionRequestValidator : 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; private const double MinSizeMeters = 100.0; private const double MaxSizeMeters = 10000.0; private const int MinZoom = 0; private const int MaxZoom = 22; public RegionRequestValidator() { RuleFor(req => req.Id) .NotEmpty() .WithMessage("`id` must be a non-zero GUID (the caller's idempotency key)."); RuleFor(req => req.Lat) .InclusiveBetween(MinLat, MaxLat) .WithMessage($"`lat` must be between {MinLat} and {MaxLat}."); RuleFor(req => req.Lon) .InclusiveBetween(MinLon, MaxLon) .WithMessage($"`lon` must be between {MinLon} and {MaxLon}."); RuleFor(req => req.SizeMeters) .InclusiveBetween(MinSizeMeters, MaxSizeMeters) .WithMessage($"`sizeMeters` must be between {MinSizeMeters} and {MaxSizeMeters} meters."); RuleFor(req => req.ZoomLevel) .InclusiveBetween(MinZoom, MaxZoom) .WithMessage($"`zoomLevel` must be between {MinZoom} and {MaxZoom} (slippy-map range)."); } }