mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-22 12:01:14 +00:00
[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>
This commit is contained in:
@@ -88,7 +88,7 @@ The N-source storage contract is authoritative in `_docs/02_document/contracts/d
|
||||
|
||||
| Config | Development | Production |
|
||||
|--------|-------------|------------|
|
||||
| Database | localhost:5432 (Docker) | Container network `db:5432` |
|
||||
| Database | localhost:5433 (Docker) | Container network `db:5432` |
|
||||
| Secrets | appsettings.Development.json | Environment variables |
|
||||
| Logging | Console + File | File (./logs/) |
|
||||
| API URL | http://localhost:5100 | http://0.0.0.0:5100 |
|
||||
@@ -200,3 +200,28 @@ The authoritative source/flight markers are the `tiles.source` and `tiles.flight
|
||||
**Decision**: Use `IHostedService` implementations that consume from the in-process queue.
|
||||
|
||||
**Consequences**: Clean separation of request handling and processing; lifecycle managed by the host.
|
||||
|
||||
## 9. Input Validation (AZ-795)
|
||||
|
||||
Every public HTTP endpoint MUST reject malformed or out-of-range payloads with HTTP 400 + RFC 7807 `ValidationProblemDetails`. The shared infrastructure landed in AZ-795 (cycle 7) is two collaborating layers:
|
||||
|
||||
1. **Deserializer-level rejection** — `JsonSerializerOptions.UnmappedMemberHandling.Disallow` configured in `Program.cs` (`ConfigureHttpJsonOptions`) catches unknown fields, type mismatches, and malformed JSON. The framework wraps the resulting `JsonException` in `BadHttpRequestException`; `GlobalExceptionHandler` extracts the JSON path and emits a structured `ValidationProblemDetails` body.
|
||||
2. **Business-rule rejection** — `FluentValidation` 12.0.0 validators registered via `AddValidatorsFromAssemblyContaining<Program>()` and wired through the generic `ValidationEndpointFilter<T>` (`SatelliteProvider.Api/Validators/ValidationEndpointFilter.cs`). Endpoints opt in via `RouteHandlerBuilder.WithValidation<T>()`; the filter calls `Results.ValidationProblem(result.ToDictionary())` on failure.
|
||||
|
||||
Both layers produce the wire shape documented in `_docs/02_document/contracts/api/error-shape.md` (v1.0.0).
|
||||
|
||||
### Validator coverage
|
||||
|
||||
| Endpoint | Request DTO | Validator | Status | Owning task |
|
||||
|----------|-------------|-----------|--------|-------------|
|
||||
| `POST /api/satellite/tiles/inventory` | `TileInventoryRequest` | `InventoryRequestValidator` | covered | AZ-796 (cycle 7) |
|
||||
| `GET /tiles/{z}/{x}/{y}` | route params | (route-constraint only — `:int` covers types; AZ-795 deserializer guards body shape on POST endpoints only) | covered by route-constraint | AZ-487 (cycle 1, JWT gate) |
|
||||
| `GET /api/satellite/tiles/latlon` | query params | (query-binding type checks via `[FromQuery]`; future AZ-795 child task to add explicit FluentValidation) | partial | future AZ-795 child |
|
||||
| `POST /api/satellite/upload` | `UavTileBatchUploadRequest` (multipart) | (envelope-level validation in `UavTileUploadHandler`; future AZ-795 child to formalize as FluentValidation) | partial | future AZ-795 child |
|
||||
| `POST /api/satellite/request` | `RequestRegionRequest` | (inline `SizeMeters` range check; future AZ-795 child) | partial | future AZ-795 child |
|
||||
| `POST /api/satellite/route` | `CreateRouteRequest` | (typed `ArgumentException` path → 400; future AZ-795 child) | partial | future AZ-795 child |
|
||||
| `GET /api/satellite/region/{id:guid}` | route param | (route-constraint `:guid`) | covered by route-constraint | — |
|
||||
| `GET /api/satellite/route/{id:guid}` | route param | (route-constraint `:guid`) | covered by route-constraint | — |
|
||||
| `GET /api/satellite/tiles/mgrs` | (stub) | n/a — returns 501 | n/a | AZ-356 |
|
||||
|
||||
The `partial` rows are tracked under the AZ-795 epic; per-endpoint child tickets to be filed by parent-suite team after enumerating the surface from the OpenAPI spec.
|
||||
|
||||
Reference in New Issue
Block a user