Files
satellite-provider/_docs/02_document/modules/common_dtos.md
T
Oleksandr Bezdieniezhnykh 61612044fb [AZ-503] [AZ-504] Cycle 5 Steps 11-15 sync
Wrap up cycle 5 verification + documentation:
- Steps 10/11 wrap-up reports (implementation_completeness +
  implementation_report) for the AZ-503-foundation + AZ-504 batch.
- Step 12 test-spec sync: AZ-503-foundation/AZ-504 ACs appended;
  AZ-505 deferred ACs recorded.
- Step 13 update-docs: architecture, data-model, glossary, module-
  layout, uav-tile-upload contract (v1.1.0), DataAccess + Services
  + Tests module docs synced; new common_uuidv5.md module doc.
- Step 14 security audit: PASS_WITH_WARNINGS; 0 new Critical/High;
  2 new Low informational (F1 flightId provenance, F2 pgcrypto
  deploy gap).
- Step 15 performance test: PASS_WITH_INFRA_WARNINGS; PT-08
  passed twice (AZ-504 fix verified); PT-01/02 failed due to
  recurring local Docker/colima DNS cold-start (not an app
  regression). Cycle-3 perf-harness leftover stays OPEN with
  replay #5 documented.
- Autodev state moved to Step 16 (Deploy).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 18:01:27 +03:00

140 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Module: Common/DTO
## Purpose
Data transfer objects used across all layers — API requests/responses, inter-service communication, and queue messages.
## Public Interface
### GeoPoint
Geographic coordinate with tolerance-based equality.
- `Lat` (double): latitude, JSON property `"lat"`
- `Lon` (double): longitude, JSON property `"lon"`
- Constructor: `GeoPoint()`, `GeoPoint(double lat, double lon)`
- Equality: two points are equal if both coordinates differ by less than `0.00005` (PRECISION_TOLERANCE)
- Operator overloads: `==`, `!=`
### Direction
Result of a directional calculation between two points.
- `Distance` (double): distance in meters
- `Azimuth` (double): bearing in degrees (0360)
### SatTile
Represents a single map tile with its spatial bounds.
- `X`, `Y` (int): tile coordinates in the slippy map scheme
- `Zoom` (int): zoom level
- `LeftTop`, `BottomRight` (GeoPoint): computed bounding box corners (via `GeoUtils.TileToWorldPos`)
- `Url` (string): download URL
- `FileName → string`: formatted as `{X}.{Y}.{Zoom}.jpg`
### TileMetadata
Metadata about a stored tile (mirrors `TileEntity` but without DB-specific concerns).
- `Id` (Guid), `TileZoom`, `TileX`, `TileY` (int), `Latitude`, `Longitude` (double)
- `TileSizeMeters` (double), `TileSizePixels` (int), `ImageType` (string)
- `Version` (int?), `FilePath` (string)
- `CreatedAt`, `UpdatedAt` (DateTime)
### RegionRequest
Queue message for async region processing.
- `Id` (Guid), `Latitude`, `Longitude` (double), `SizeMeters` (double)
- `ZoomLevel` (int), `StitchTiles` (bool)
### RegionStatus
Response DTO for region status queries.
- `Id` (Guid), `Status` (string), `CsvFilePath`, `SummaryFilePath` (string?)
- `TilesDownloaded`, `TilesReused` (int), `CreatedAt`, `UpdatedAt` (DateTime)
### RoutePoint
Input point in a route creation request.
- `Latitude` (double, JSON: `"lat"`), `Longitude` (double, JSON: `"lon"`)
### RoutePointDto
Output point in a route response (includes computed fields).
- `Latitude`, `Longitude` (double), `PointType` (string: "start"/"end"/"action"/"intermediate")
- `SequenceNumber`, `SegmentIndex` (int), `DistanceFromPrevious` (double?)
### CreateRouteRequest
API request body for route creation.
- `Id` (Guid), `Name` (string), `Description` (string?)
- `RegionSizeMeters` (double), `ZoomLevel` (int)
- `Points` (List\<RoutePoint\>), `Geofences` (Geofences?)
- `RequestMaps` (bool), `CreateTilesZip` (bool)
### RouteResponse
API response for route queries.
- All fields from the route entity plus `Points` (List\<RoutePointDto\>)
- `MapsReady` (bool), `TilesZipPath` (string?)
### GeofencePolygon
Axis-aligned bounding box defined by NW and SE corners.
- `NorthWest` (GeoPoint?), `SouthEast` (GeoPoint?)
### Geofences
Container for multiple geofence polygons.
- `Polygons` (List\<GeofencePolygon\>)
### UavTileMetadata (added AZ-488, extended AZ-503)
Per-tile metadata payload inside a UAV batch upload (`POST /api/satellite/upload`). Indexed-correlated with the multipart `IFormFileCollection`.
- `Latitude`, `Longitude` (double)
- `TileZoom` (int)
- `TileSizeMeters` (double)
- `CapturedAt` (DateTime, UTC; subject to AZ-488 Rule 4 future-skew / age checks)
- `FlightId` (Guid?, JSON: `"flightId"`) — AZ-503 optional flight identifier. When set, the per-item `tiles.id` becomes `Uuidv5(TileNamespace, "{z}/{x}/{y}/uav/{flightId}")`, the on-disk path is `./tiles/uav/{flightId}/{z}/{x}/{y}.jpg`, and the UPSERT conflict key separates this row from rows belonging to other flights at the same cell. When `null`, the per-item id uses the zero-UUID `00000000-0000-0000-0000-000000000000` placeholder and the on-disk path uses the literal `none` segment (`./tiles/uav/none/{z}/{x}/{y}.jpg`). The placeholder UUID is purely a key-space marker — it never lands in the `flight_id` column (which stays `NULL`); the UPSERT uses `COALESCE(flight_id, '00000000-...')` for the conflict check.
### UavTileBatchMetadataPayload (added AZ-488)
JSON envelope deserialized from the `metadata` form field of a UAV batch upload.
- `Items` (IReadOnlyList\<UavTileMetadata\>)
### UavTileBatchUploadResponse (added AZ-488)
Wire response for `POST /api/satellite/upload`. Returned with HTTP 200 regardless of per-item outcomes; envelope-level failures (auth, oversize, deserialization) bypass this shape.
- `Items` (List\<UavTileUploadResultItem\>)
### UavTileUploadResultItem (added AZ-488)
Per-item result inside `UavTileBatchUploadResponse`.
- `Index` (int): zero-based index into the request batch.
- `Status` (string): one of `UavTileUploadStatus.Accepted` / `UavTileUploadStatus.Rejected`.
- `TileId` (Guid?): set on accept (matches the new/updated `tiles.id`); null on reject.
- `RejectReason` (string?): closed-enum reason code from `UavTileRejectReasons`; null on accept.
- `RejectDetails` (string?): short human-readable note. MUST NOT leak server-internal paths / exception types / hostnames (AZ-488 Security NFR; covered by SEC-11).
### UavTileUploadStatus (added AZ-488, static string constants)
- `Accepted = "accepted"`
- `Rejected = "rejected"`
### UavTileRejectReasons (added AZ-488, static string constants — closed enumeration v1.0.0)
Authoritative reject-reason codes for the UAV upload quality gate. Adding a new code requires a minor-version bump of `_docs/02_document/contracts/api/uav-tile-upload.md`.
- `InvalidFormat = "INVALID_FORMAT"` — Rule 1 (content-type or JPEG magic bytes).
- `SizeOutOfBand = "SIZE_OUT_OF_BAND"` — Rule 2 (bytes outside `[MinBytes, MaxBytes]`).
- `WrongDimensions = "WRONG_DIMENSIONS"` — Rule 3 (image width/height ≠ `MapConfig.TileSizePixels`).
- `CapturedAtFuture = "CAPTURED_AT_FUTURE"` — Rule 4 (timestamp ahead of now + `CapturedAtFutureSkewSeconds`).
- `CapturedAtTooOld = "CAPTURED_AT_TOO_OLD"` — Rule 4 (timestamp older than `MaxAgeDays`).
- `ImageTooUniform = "IMAGE_TOO_UNIFORM"` — Rule 5 (luminance variance below `MinLuminanceVariance`).
- `StorageFailure = "STORAGE_FAILURE"` — reserved for the orphan-row-recovery path when the on-disk write succeeds but the DB UPSERT fails; surfaced per-item without failing the envelope (AZ-488 Reliability NFR).
## Internal Logic
- `GeoPoint` uses a precision tolerance of `0.00005` degrees (~5.5 meters) for equality comparison.
- `SatTile` eagerly computes its bounding box corners on construction by calling `GeoUtils.TileToWorldPos`.
## Dependencies
- `GeoPoint`, `Direction` — no imports
- `SatTile``SatelliteProvider.Common.Utils.GeoUtils`
- All others — no internal dependencies (or only `System.Text.Json.Serialization`)
## Consumers
- All services, repositories, and API endpoints consume these DTOs
- `RegionRequest` is the message type for `IRegionRequestQueue`
## Data Models
These ARE the data models (DTOs). They map closely to the database entities but are decoupled from the persistence layer.
## Configuration
None consumed directly.
## External Integrations
None.
## Security
None.
## Tests
No dedicated DTO tests.