# Flow F2 — Mission create / read / update > Post-rename. Today: `[Route("flights")]`, `Flight*` files. ## Description Mission CRUD excluding delete (delete is the cross-service cascade in F3). Create / update validate that the referenced `vehicle_id` exists; list (`GET /missions`) is the only paginated endpoint in this service. ## Preconditions - Service is running, schema in place (F6). - Caller holds JWT with `permissions=FL` (F5). - For create / update with `VehicleId`: the referenced vehicle exists (F1). ## Sequence Diagram (POST `/missions`) ```mermaid sequenceDiagram autonumber participant UI as Operator UI participant Identity as 05_identity participant Errs as 06_http_conventions participant Ctrl as MissionsController participant MS as MissionService participant DB as 04_persistence (postgres-local) UI->>Identity: POST /missions + JWT + { Name, VehicleId } Identity-->>Ctrl: authorized (policy "FL") Ctrl->>MS: CreateMission(req) MS->>DB: SELECT 1 FROM vehicles WHERE id = @VehicleId alt Vehicle missing DB-->>MS: 0 rows MS-->>Errs: throw ArgumentException("VehicleId not found") note right of MS: Spec says 404; code returns 400. Carry-forward. Errs-->>UI: 400 Bad Request (PascalCase error envelope) else Vehicle exists DB-->>MS: 1 row MS->>DB: INSERT INTO missions (id, name, vehicle_id, created_date) VALUES (...) DB-->>MS: row inserted MS-->>Ctrl: Mission entity Ctrl-->>UI: 201 Created + Mission (Vehicle / Waypoints serialize as null / [] — no eager load) end ``` ## Flowchart (GET `/missions` paginated) ```mermaid flowchart TD Start([GET /missions?Name=&FromDate=&ToDate=&Page=&PageSize=]) --> Auth{JWT + FL valid?} Auth -->|no| Reject([401 / 403]) Auth -->|yes| Build[Build LINQ predicate from optional filters] Build --> Count[COUNT * over filtered set] Count --> Page[SELECT ... ORDER BY created_date DESC LIMIT pageSize OFFSET] Page --> Wrap[Wrap in PaginatedResponse Items, TotalCount, Page, PageSize] Wrap --> Done([200 OK + envelope, PascalCase]) ``` ## Data Flow | Step | From | To | Data | Format | |------|------|----|------|--------| | 1 | UI | `MissionsController` | `CreateMissionRequest` / `UpdateMissionRequest` / `GetMissionsQuery` | JSON / query string (PascalCase) | | 2 | `MissionService` | `vehicles` table | existence check `SELECT 1` | SQL | | 3 | `MissionService` | `missions` table | INSERT / UPDATE / SELECT | SQL | | 4 | `MissionService` | UI | `Mission` entity / `PaginatedResponse` | JSON (PascalCase) | ## Error Scenarios | Error | Where | Detection | Recovery | |-------|-------|-----------|----------| | `VehicleId` missing on create / update | `MissionService.CreateMission` / `UpdateMission` | existence check returns false | `ArgumentException` → `400` (spec wants `404` — minor divergence, B-set carry-forward) | | TOCTOU: vehicle deleted between existence check and insert | `MissionService.CreateMission` | FK constraint violation | Npgsql `PostgresException` → middleware → `500`. UX gap (should be `400`); rare in practice | | Mission not found | `MissionService.GetMission` / `UpdateMission` | entity lookup `null` | `KeyNotFoundException` → `404` | | Page / PageSize out of range | None enforced | n/a | LinqToDB `Skip(negative)` / `Take(0)` returns empty set; no error returned to client | ## Performance Expectations | Metric | Target | Notes | |--------|--------|-------| | End-to-end latency (single mission) | <15ms typical | Two round-trips on create (existence check + insert); one on read | | Paginated list latency | <30ms typical for ≤1000 rows | No index on `created_date` — full scan + sort. Add `ix_missions_created_date` if list latency becomes an issue | | Throughput | Operator-paced | Not load-tested |