Files
satellite-provider/_docs/02_document/modules/api_program.md
T
Oleksandr Bezdieniezhnykh 1802d32107 [AZ-488] UAV tile batch upload + 5-rule quality gate
Replaces the 501 stub at POST /api/satellite/upload with a multipart
batch endpoint that ingests UAV-captured tiles, runs each item through
a 5-rule quality gate, and persists accepted tiles via the AZ-484
multi-source storage path with source='uav'.

Quality gate (in fixed order, first failure wins): JPEG format
(content-type + magic), size band 5 KiB-5 MiB, exact 256x256
dimensions, captured-at age (no future >30 s skew, no older than
7 days), luminance variance on 32x32 downsample. Closed reject-reason
enumeration in v1.0.0 contract.

Authorization: custom PermissionsRequirement / PermissionsAuthorization
Handler that reads the JWT `permissions` claim (tolerates both
repeated-string and JSON-array shapes). Endpoint protected by
RequiresGpsPermission policy; 401 without token, 403 without GPS perm.

Persistence: file-first to ./tiles/uav/{z}/{x}/{y}.jpg, then
ITileRepository.InsertAsync UPSERT (per-source UPSERT contract from
AZ-484). Per-item failures reported in response without aborting the
batch. Kestrel MaxRequestBodySize and FormOptions limits set to
MaxBatchSize x MaxBytes (default 100 x 5 MiB = 500 MiB).

New frozen contract: _docs/02_document/contracts/api/uav-tile-upload.md
v1.0.0. PT-08 NFR added to performance-tests.md as Deferred (harness
work tracked in PT-07 leftover, per AZ-488 § Risk 4).

Tests: 11 quality-gate unit tests, 5 handler unit tests, 3 file-path
unit tests, 12 permission-handler unit tests, 7 integration tests
(AC-1..AC-6, AC-8). All 253 unit tests + smoke integration suite
green.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 23:50:49 +03:00

7.2 KiB
Raw Blame History

Module: Api/Program.cs

Purpose

Application entry point. Configures DI container, sets up middleware, defines minimal API endpoints, runs database migrations on startup, and starts background services.

Public Interface

API Endpoints

Method Route Handler Description
GET /tiles/{z}/{x}/{y} ServeTile Slippy map tile server with in-memory caching
GET /api/satellite/tiles/latlon GetTileByLatLon Download single tile by lat/lon/zoom
GET /api/satellite/tiles/mgrs GetSatelliteTilesByMgrs MGRS stub (returns empty)
POST /api/satellite/upload UploadUavTileBatch UAV tile batch upload (AZ-488) — multipart envelope, 5-rule quality gate, per-source UPSERT with source='uav'. Requires the RequiresGpsPermission policy.
POST /api/satellite/request RequestRegion Queue region for async tile processing
GET /api/satellite/region/{id} GetRegionStatus Get region processing status
POST /api/satellite/route CreateRoute Create route with intermediate points
GET /api/satellite/route/{id} GetRoute Get route with all points

Local Records (defined in Program.cs)

  • GetSatelliteTilesResponse, SatelliteTile — MGRS response stubs
  • DownloadTileResponse — tile download response
  • RequestRegionRequest — region request body
  • ParameterDescriptionFilter — Swagger operation filter

Api/DTOs (AZ-488)

  • UavTileBatchUploadRequest — multipart envelope with metadata (JSON string) and files (IFormFileCollection)

Common/DTO (AZ-488)

  • UavTileMetadata, UavTileBatchMetadataPayload — per-item metadata + envelope shape
  • UavTileBatchUploadResponse, UavTileUploadResultItem — per-item response shape
  • UavTileUploadStatus, UavTileRejectReasons — string-constant enumerations exposed in the v1.0.0 contract

Internal Logic

DI Registration

  1. Serilog configured from appsettings.json
  2. Connection string extracted from ConnectionStrings:DefaultConnection
  3. Config bindings: MapConfig, StorageConfig, ProcessingConfig, UavQualityConfig (AZ-488)
  4. Request size limits (AZ-488): KestrelServerOptions.Limits.MaxRequestBodySize and FormOptions.MultipartBodyLengthLimit are set to UavQualityConfig.MaxBatchSize × UavQualityConfig.MaxBytes (default 100 × 5 MiB = 500 MiB) so an oversized UAV batch is rejected at the framework layer before reaching the handler.
  5. Singletons: repositories (TileRepository, RegionRepository, RouteRepository), GoogleMapsDownloaderV2, ITileService, IRegionService, IRouteService, IUavTileQualityGate, IUavTileUploadHandler (AZ-488)
  6. IRegionRequestQueue with configurable capacity
  7. Hosted services: RegionProcessingService, RouteProcessingService
  8. CORS policy: TilesCors — configured origins from CorsConfig:AllowedOrigins, falls back to allow-any
  9. JSON options: camelCase, case-insensitive
  10. JWT authentication (AZ-487): AddSatelliteJwt(builder.Configuration) (extension in SatelliteProvider.Api.Authentication) registers JwtBearer with TokenValidationParameters set per the suite auth contract (signature + lifetime, no issuer/audience validation, 30 s clock skew, ≥ 32-byte HMAC key). Followed by AddAuthorization with the RequiresGpsPermission policy (AZ-488). The PermissionsAuthorizationHandler singleton supports both repeated-string and JSON-array shapes for the permissions claim.

Startup

  1. Database migration via DatabaseMigrator.RunMigrations() — throws on failure
  2. Creates tiles and ready directories
  3. Swagger enabled in Development mode
  4. Middleware chain (order matters): UseExceptionHandlerUseHttpsRedirectionUseCors("TilesCors")UseAuthenticationUseAuthorization → endpoint mapping.
  5. Every MapGet/MapPost endpoint is decorated with .RequireAuthorization(); the framework returns 401 before the handler runs for any anonymous, expired, or invalid-signature request.

ServeTile Handler

  1. Checks IMemoryCache for tile bytes (1h absolute, 30min sliding expiration)
  2. If cache miss: queries ITileRepository.GetByTileCoordinatesAsync
  3. If no DB record: downloads tile via GoogleMapsDownloaderV2.DownloadSingleTileAsync, creates TileEntity, inserts
  4. Returns image bytes with cache headers (Cache-Control: public, max-age=86400)

GetTileByLatLon Handler

Downloads a tile, persists it, returns metadata as DownloadTileResponse.

RequestRegion Handler

Validates size (10010000m), delegates to IRegionService.RequestRegionAsync.

UploadUavTileBatch Handler (AZ-488)

Buffers each IFormFile into memory, packages them as UavUploadFile records (filename, content-type, bytes), and delegates to IUavTileUploadHandler.HandleAsync. Envelope-level errors (mismatched batch, oversized batch, malformed metadata) are surfaced as HTTP 400 ProblemDetails; per-item rejects are returned in the HTTP 200 response payload. The endpoint is protected by .RequireAuthorization(SatellitePermissions.UavUploadPolicy) so 401 (no token) and 403 (no GPS permission) are returned before the handler runs.

Dependencies

All project references: Common, DataAccess, Services. NuGet: Serilog.AspNetCore, Swashbuckle.AspNetCore, Microsoft.AspNetCore.OpenApi, Microsoft.AspNetCore.Authentication.JwtBearer (8.0.21, AZ-487), SixLabors.ImageSharp, Newtonsoft.Json.

Consumers

  • HTTP clients (external)
  • Integration tests (via HTTP)

Data Models

Defines several local request/response records that are not shared with other projects.

Configuration

All configuration sections are consumed here:

  • ConnectionStrings:DefaultConnection
  • MapConfig, StorageConfig, ProcessingConfig
  • UavQuality (AZ-488) — MinBytes, MaxBytes, MaxAgeDays, CapturedAtFutureSkewSeconds, MinLuminanceVariance, MaxBatchSize, LuminanceSampleSize. Drives the 5-rule quality gate AND the per-request body-size limits.
  • CorsConfig:AllowedOrigins
  • Jwt:Secret — HMAC-SHA256 signing key for JWT validation (AZ-487). Resolution: JWT_SECRET env var (preferred, opaque production secret) → Jwt:Secret configuration key (appsettings.Development.json placeholder only). Startup fails fast if the resolved value is unset, empty, or shorter than 32 bytes.
  • Serilog section

External Integrations

  • Google Maps (indirectly via GoogleMapsDownloaderV2)
  • PostgreSQL (via repositories and DatabaseMigrator)
  • File system (./tiles/, ./ready/)

Security

  • CORS configured (permissive by default when no origins specified)
  • Swagger only in Development; Bearer token "Authorize" button registered via AddSecurityDefinition/AddSecurityRequirement (AZ-487)
  • HTTPS redirection enabled
  • JWT bearer authentication (AZ-487) — every endpoint requires a valid HS256-signed token. Anonymous, expired, or signature-tampered requests return 401 before the handler runs.
  • Permission-claim policies (AZ-488) — POST /api/satellite/upload is wrapped in .RequireAuthorization(SatellitePermissions.UavUploadPolicy). The PermissionsAuthorizationHandler reads the permissions claim (repeated-string OR JSON-array shape) and returns 403 if GPS is not present.

Tests

Integration tests exercise all endpoints. Unit test project has only a dummy test.