mirror of
https://github.com/azaion/missions.git
synced 2026-06-21 13:01:06 +00:00
7025f4d075
Updated JWT authentication to use configuration values instead of hardcoded secrets, improving security and flexibility. Enhanced CORS policy to conditionally allow origins based on configuration settings, with logging for permissive defaults. Updated README to reflect project renaming and clarify service context.
155 lines
7.8 KiB
Markdown
155 lines
7.8 KiB
Markdown
# Module: `Azaion.Missions.Database.Entities`
|
|
|
|
**Files (7)**: `Vehicle.cs`, `Mission.cs`, `Waypoint.cs`, `MapObject.cs`, `Media.cs`, `Annotation.cs`, `Detection.cs`
|
|
|
|
> **NOTE (forward-looking)**: this doc reflects the post-rename state. Today's source still has `Aircraft.cs`, `Flight.cs`, `Orthophoto.cs`, `GpsCorrection.cs`. The renames + GPS-Denied removal are tracked under Jira AZ-EPIC children B6 (domain rename) and B7 (GPS-Denied removal).
|
|
|
|
## Purpose
|
|
|
|
LinqToDB row-mapping classes. Each entity uses `[Table("snake_case_name")]` + `[Column("snake_case")]` + `[PrimaryKey]` to map to a PostgreSQL table. Two entities (`Mission`, `Waypoint`) include `[Association]`-based navigation; the rest are flat row maps.
|
|
|
|
## Public Interface
|
|
|
|
### Vehicle (table `vehicles`)
|
|
|
|
```csharp
|
|
[Table("vehicles")]
|
|
public class Vehicle {
|
|
[PrimaryKey, Column("id")] public Guid Id;
|
|
[Column("type")] public VehicleType Type; // Plane | Copter | UGV | GuidedMissile
|
|
[Column("model")] public string Model = "";
|
|
[Column("name")] public string Name = "";
|
|
[Column("fuel_type")] public FuelType FuelType;
|
|
[Column("battery_capacity")] public decimal BatteryCapacity;
|
|
[Column("engine_consumption")] public decimal EngineConsumption;
|
|
[Column("engine_consumption_idle")] public decimal EngineConsumptionIdle;
|
|
[Column("is_default")] public bool IsDefault;
|
|
}
|
|
```
|
|
|
|
### Mission (table `missions`)
|
|
|
|
```csharp
|
|
[Table("missions")]
|
|
public class Mission {
|
|
[PrimaryKey, Column("id")] public Guid Id;
|
|
[Column("created_date")] public DateTime CreatedDate;
|
|
[Column("name")] public string Name = "";
|
|
[Column("vehicle_id")] public Guid VehicleId;
|
|
|
|
[Association(ThisKey=VehicleId, OtherKey=Vehicle.Id)] public Vehicle? Vehicle;
|
|
[Association(ThisKey=Id, OtherKey=Waypoint.MissionId)] public List<Waypoint> Waypoints = [];
|
|
}
|
|
```
|
|
|
|
### Waypoint (table `waypoints`)
|
|
|
|
```csharp
|
|
[Table("waypoints")]
|
|
public class Waypoint {
|
|
[PrimaryKey, Column("id")] public Guid Id;
|
|
[Column("mission_id")] public Guid MissionId;
|
|
[Column("lat")] public decimal? Lat;
|
|
[Column("lon")] public decimal? Lon;
|
|
[Column("mgrs")] public string? Mgrs;
|
|
[Column("waypoint_source")] public WaypointSource WaypointSource;
|
|
[Column("waypoint_objective")] public WaypointObjective WaypointObjective;
|
|
[Column("order_num")] public int OrderNum;
|
|
[Column("height")] public decimal Height;
|
|
|
|
[Association(ThisKey=MissionId, OtherKey=Mission.Id)] public Mission? Mission;
|
|
}
|
|
```
|
|
|
|
### MapObject (table `map_objects`)
|
|
|
|
```csharp
|
|
[Table("map_objects")]
|
|
public class MapObject {
|
|
[PrimaryKey, Column("id")] public Guid Id;
|
|
[Column("mission_id")] public Guid MissionId;
|
|
[Column("h3_index")] public string H3Index = ""; // Uber H3 hex grid
|
|
[Column("mgrs")] public string Mgrs = "";
|
|
[Column("lat")] public decimal? Lat;
|
|
[Column("lon")] public decimal? Lon;
|
|
[Column("class_num")] public int ClassNum;
|
|
[Column("label")] public string Label = "";
|
|
[Column("size_width_m")] public decimal SizeWidthM;
|
|
[Column("size_length_m")] public decimal SizeLengthM;
|
|
[Column("confidence")] public decimal Confidence;
|
|
[Column("object_status")] public ObjectStatus ObjectStatus;
|
|
[Column("first_seen_at")] public DateTime FirstSeenAt;
|
|
[Column("last_seen_at")] public DateTime LastSeenAt;
|
|
}
|
|
```
|
|
|
|
### Media / Annotation / Detection (cross-service stubs -- see "Notes / Smells")
|
|
|
|
```csharp
|
|
[Table("media")]
|
|
public class Media {
|
|
[PrimaryKey, Column("id")] public string Id = ""; // TEXT primary key
|
|
[Column("waypoint_id")] public Guid? WaypointId;
|
|
}
|
|
|
|
[Table("annotations")]
|
|
public class Annotation {
|
|
[PrimaryKey, Column("id")] public string Id = ""; // TEXT
|
|
[Column("media_id")] public string MediaId = ""; // TEXT FK to media.id
|
|
}
|
|
|
|
[Table("detection")] // SINGULAR table name -- diverges from every other entity (owned by detection pipeline)
|
|
public class Detection {
|
|
[PrimaryKey, Column("id")] public Guid Id;
|
|
[Column("annotation_id")] public string AnnotationId = "";
|
|
}
|
|
```
|
|
|
|
## Internal Logic
|
|
|
|
Pure POCOs. The only behavior comes from LinqToDB attribute mapping (`[Table]`, `[Column]`, `[PrimaryKey]`, `[Association]`).
|
|
|
|
## Dependencies
|
|
|
|
- `LinqToDB.Mapping` (NuGet)
|
|
- `Azaion.Missions.Enums` (for `Vehicle`, `Waypoint`, `MapObject`)
|
|
|
|
## Consumers
|
|
|
|
- `Database.AppDataConnection` -- exposes `ITable<T>` for each entity.
|
|
- `Database.DatabaseMigrator` -- implicitly (defines DDL for the same names; does NOT reference entity types).
|
|
- `Services.VehicleService`, `Services.MissionService`, `Services.WaypointService` -- entity types are returned/constructed.
|
|
|
|
## Data Model Highlights
|
|
|
|
```
|
|
vehicles --< missions --< waypoints --? media --< annotations --< detection
|
|
\--< map_objects
|
|
```
|
|
|
|
- `Mission` is the central aggregate root: most domain rows hang off `mission_id`.
|
|
- `Waypoint` is a sub-aggregate of `Mission` and is the join point for `Media`.
|
|
- `MapObject` is detection output (class_num + confidence + spatial index) tied to a mission, NOT to a specific waypoint. **Schema is owned by this service, but rows are written by `autopilot`** (per `../../suite/_docs/06_autopilot_design.md`).
|
|
|
|
## Cross-service stubs
|
|
|
|
`Media`, `Annotation`, `Detection` are intentional read-only stubs -- only `Id` and one foreign key each. They're queried/deleted by `MissionService.DeleteMission` and `WaypointService.DeleteWaypoint` but never written by this service. Schema-wise they are owned by other suite components (`annotations` for `media` + `annotations`, the detection pipeline for `detection`), per `../../suite/_docs/00_top_level_architecture.md` and `../../suite/_docs/01_annotations.md`. The shared edge-PostgreSQL pattern means each service migrates its own tables but all services see the full schema.
|
|
|
|
These three are deliberately NOT in `DatabaseMigrator.Sql` -- their schema is created by the owning services on the same shared local PostgreSQL.
|
|
|
|
## Configuration / External Integrations / Security
|
|
|
|
None directly. Persistence is delegated to LinqToDB + Npgsql.
|
|
|
|
## Tests
|
|
|
|
None present.
|
|
|
|
## Notes / Smells
|
|
|
|
1. **`Detection` table singular** while `vehicles`, `missions`, `waypoints`, `map_objects`, `media`, `annotations` are plural. Owned by another service -- naming is THEIR call to make consistent.
|
|
2. **Mixed PK types**: `Vehicle`, `Mission`, `Waypoint`, `MapObject`, `Detection` use `Guid`; `Media`, `Annotation` use `string` (TEXT, XxHash64-based per `../../suite/_docs/00_database_schema.md`).
|
|
3. **No domain methods** -- all business rules (e.g., the "default vehicle" exclusivity in `VehicleService`) live in services, not in the entities. Consistent and intentional for a thin-data-model approach.
|
|
4. **`Media.WaypointId` is nullable** while every other foreign key here is not. Suggests `Media` can attach to a non-waypoint context (e.g., mission-level media); enforcement is on `annotations`'s side.
|
|
5. **Geopoint divergence (carry to verification log)**: spec stores `Waypoints.GPS` as a single `string GPS` field with `Lat <-> MGRS` auto-conversion (per `../../suite/_docs/02_missions.md` and `../../suite/_docs/00_database_schema.md`). Code splits it into 3 separate columns. Resolution lives outside the GPS-Denied removal scope -- carry forward.
|