Files
satellite-provider/SatelliteProvider.Common/DTO/TileInventory.cs
T
Oleksandr Bezdieniezhnykh 865dfdb3b9
ci/woodpecker/push/01-test Pipeline was successful
ci/woodpecker/push/02-build-push Pipeline was successful
[AZ-794] [AZ-795] [AZ-796] Strict input validation + z/x/y rename
AZ-794: rename inventory wire fields tileZoom/tileX/tileY -> z/x/y
to match the slippy-map URL convention. Contract bumped to v2.0.0.

AZ-795: shared validation infrastructure -- FluentValidation +
ValidationEndpointFilter + GlobalValidatorConfig (camelCase paths).
GlobalExceptionHandler now converts JsonException (UnmappedMember +
JsonRequired) into RFC 7807 ValidationProblemDetails. JSON layer
hardened with UnmappedMemberHandling.Disallow + camelCase naming
policy. New error-shape.md contract.

AZ-796: InventoryRequestValidator covers 9 rules (XOR tiles vs
locationHashes, cap 1000, z 0..22, x/y in slippy bounds, hash
length/charset). 16 unit tests + 16 integration tests + a manual
curl probe script.

Adjacent fixes uncovered by the new strict layer:
- IdempotentPostTests RoutePoint payload corrected to lat/lon
  (the DTO has used JsonPropertyName for ages; previously silently
  ignored under PascalCase fallback).
- TileInventoryTests slippy x/y reduced to fit z=18 bounds.
- docker-compose.yml host port for Postgres moved 5432 -> 5433 to
  avoid sibling-project conflict; appsettings.Development + README
  + AGENTS + architecture + containerization docs aligned.

New coderule (suite + repo): API consumer-facing OpenAPI
descriptions must not contain task IDs, contract filenames, or
version-bump history -- internal change tracking belongs in
commits/contract docs/changelogs. Existing offending descriptions
in Program.cs cleaned up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 10:02:02 +03:00

84 lines
3.4 KiB
C#

using System.Text.Json.Serialization;
namespace SatelliteProvider.Common.DTO;
// AZ-505: bulk-list / inventory request envelope. Either `Tiles` OR
// `LocationHashes` is populated — never both, never neither. The handler
// converts every `(z, x, y)` coord into a `location_hash` via UUIDv5 and
// queries `tiles_leaflet_path` once. Response order matches request order.
//
// Max entries per request: see TileInventoryLimits.MaxEntriesPerRequest.
public sealed class TileInventoryRequest
{
public IReadOnlyList<TileCoord>? Tiles { get; set; }
public IReadOnlyList<Guid>? LocationHashes { get; set; }
}
// AZ-505: Slippy-map tile coordinate triple. AZ-794 (cycle 7) renamed the
// wire-format fields from `tileZoom/tileX/tileY` → `z/x/y` to align with the
// OSM / slippy-map convention already used by `GET /tiles/{z}/{x}/{y}` and
// to shave wire-size on inventory requests carrying thousands of entries.
// The C# property names (`Z`, `X`, `Y`) intentionally mirror the wire names
// 1:1 so consumers don't need to mentally translate at the deserialization
// boundary. The DataAccess `TileEntity.TileZoom/TileX/TileY` columns are
// unchanged — that's a database identity, not a wire format.
public sealed class TileCoord
{
[JsonRequired]
public int Z { get; set; }
[JsonRequired]
public int X { get; set; }
[JsonRequired]
public int Y { get; set; }
}
// AZ-505: Inventory response. Entries are returned in the SAME ORDER as the
// matching request input (per AC-1). When Request.Tiles was populated, each
// entry's `Z`/`X`/`Y` echoes the request entry; when Request.LocationHashes
// was populated, the coord triple fields are 0 (the caller already knows
// the hash and can map it back themselves). AZ-794 (cycle 7) renamed the
// coord triple to `z/x/y` to align wire format with the URL-path
// convention.
public sealed class TileInventoryResponse
{
public IReadOnlyList<TileInventoryEntry> Results { get; set; } = Array.Empty<TileInventoryEntry>();
}
// AZ-505: One entry per request input. `Present` indicates whether a row
// exists in the `tiles` table for the resolved `LocationHash`. When
// `Present == false` only `LocationHash` (and the echoed coord triple, if the
// request used coords) is populated — the rest are null.
//
// `EstimatedBytes` is intentionally absent in v1.0.0 — adding the per-row
// `stat()` cost is deferred until production profiling justifies it (see
// AZ-505 Outcome bullet 1 + Excluded list).
//
// AZ-794 (cycle 7): coord triple renamed `tileZoom/tileX/tileY` → `z/x/y`
// (contract bumped to v2.0.0).
public sealed class TileInventoryEntry
{
public int Z { get; set; }
public int X { get; set; }
public int Y { get; set; }
public Guid LocationHash { get; set; }
public bool Present { get; set; }
public Guid? Id { get; set; }
public DateTime? CapturedAt { get; set; }
public string? Source { get; set; }
public Guid? FlightId { get; set; }
public double? ResolutionMPerPx { get; set; }
}
// AZ-505: per-task constants exposed for the request validator + tests.
// Living under DTO so both the API handler and test assertions can reference
// the same value without re-deriving it.
public static class TileInventoryLimits
{
// 2x headroom over the AC-4 perf gate of 2500 tiles. Anything larger is
// rejected with HTTP 400 by the API handler.
public const int MaxEntriesPerRequest = 5000;
}