Files
Oleksandr Bezdieniezhnykh 78dea8ebab
ci/woodpecker/push/build-arm Pipeline was successful
chore: update configuration and Docker setup for JWT and test results
Enhanced the .gitignore to exclude test results and updated the Dockerfile to include a new entrypoint script for improved container initialization. Refactored JWT configuration to support additional parameters for automatic refresh intervals, ensuring better control over token management. Updated the ConfigurationResolver to enforce required environment variables without hardcoded fallbacks, enhancing security and flexibility.
2026-05-15 03:23:23 +03:00

15 KiB

Input Data Parameters — Azaion.Missions

Status: derived-from-code (autodev /document Step 6, 2026-05-14). Schemas below match the actual Database/Entities/*.cs LinqToDB mappings and DTOs/*.cs request shapes (post-B6 names). Today's source still uses pre-rename names; the doc-vs-code mapping is in _docs/02_document/04_verification_log.md § 0.


1. Configuration input (env vars)

All four required values are resolved through Infrastructure/ConfigurationResolver.csResolveRequiredOrThrow(envName, configKey). Resolution order is env var first, then IConfiguration config key, else throw InvalidOperationException at startup. There are NO hardcoded dev fallbacks anymore.

Variable Config key Type Required Resolution Format / constraints Used by
DATABASE_URL Database:Url string yes (always) ResolveRequiredOrThrow Either postgresql://user:pass@host:port/db (converted via local helper ConvertPostgresUrl; NO URL-decoding of user/password — credentials with @, :, /, % need raw Npgsql form) OR a raw Npgsql connection string Program.cs (DI registration of AppDataConnection)
JWT_ISSUER Jwt:Issuer string yes (always) ResolveRequiredOrThrow The expected iss claim value on every accepted JWT; usually the admin service's stable identifier Program.cs, Auth/JwtExtensions.csValidIssuer
JWT_AUDIENCE Jwt:Audience string yes (always) ResolveRequiredOrThrow The expected aud claim value on every accepted JWT; usually the suite-wide audience identifier shared by all backend validators Program.cs, Auth/JwtExtensions.csValidAudience
JWT_JWKS_URL Jwt:JwksUrl string yes (always) ResolveRequiredOrThrow HTTPS URL to admin's JWKS endpoint. HttpDocumentRetriever { RequireHttps = true } rejects http:// at fetch time (not at startup config resolution). Cached via ConfigurationManager<JsonWebKeySet>, refreshed on the default schedule Program.cs, Auth/JwtExtensions.cs
ASPNETCORE_ENVIRONMENT (built-in) string no ASP.NET Core convention Case-insensitive match on Production triggers the CORS strict gate in CorsConfigurationValidator Program.cs, Infrastructure/CorsConfigurationValidator.cs
CorsConfig:AllowedOrigins (config) string list conditionally required IConfiguration.GetSection("CorsConfig").Get<CorsConfig>() List of allowed origins. In Production, MUST be non-empty OR AllowAnyOrigin=true, else startup throws Program.cs, Infrastructure/CorsConfigurationValidator.cs
CorsConfig:AllowAnyOrigin (config) bool no same Opt-in to permissive CORS in production explicitly (use sparingly) same
AZAION_REVISION string no Dockerfile ARG baked from CI_COMMIT_SHA git SHA Dockerfile only; surfaced via docker inspect
ASPNETCORE_URLS string no ASP.NET Core convention URL list (default http://+:8080) ASP.NET Core host

Important: The legacy JWT_SECRET env var is no longer consulted. The ADR-005 "dev fallback secret silently accepted in production" failure mode is structurally eliminated; only the unconditional-Swagger branch of ADR-005 survives.

2. HTTP request DTOs (post-B6 shapes)

2.1 Vehicle (/vehicles)

public class CreateVehicleRequest {
    public VehicleType Type { get; set; }            // enum int: Plane=0, Copter=1, UGV=2, GuidedMissile=3
    public string      Model { get; set; } = "";
    public string      Name { get; set; } = "";
    public FuelType    FuelType { get; set; }        // enum int: Electric=0, Gasoline=1, Diesel=2
    public decimal     BatteryCapacity { get; set; }
    public decimal     EngineConsumption { get; set; }
    public decimal     EngineConsumptionIdle { get; set; }
    public bool        IsDefault { get; set; }
}

public class UpdateVehicleRequest {                  // all properties nullable -- partial update
    public VehicleType? Type;
    public string?      Model;
    public string?      Name;
    public FuelType?    FuelType;
    public decimal?     BatteryCapacity;
    public decimal?     EngineConsumption;
    public decimal?     EngineConsumptionIdle;
    public bool?        IsDefault;
}

public class GetVehiclesQuery {
    public string? Name { get; set; }                // case-INSENSITIVE contains (LOWER(name) LIKE %lower(input)%)
    public bool?   IsDefault { get; set; }           // exact match
}

public class SetDefaultRequest {
    public bool IsDefault { get; set; }
}

Validation: NONE today. No [Required], no [Range], no min-length. Empty Name, negative BatteryCapacity, out-of-range enum int values are accepted. Carry-forward improvement.

2.2 Mission (/missions)

public class CreateMissionRequest {
    public Guid     VehicleId { get; set; }
    public string   Name { get; set; } = "";
    public DateTime? CreatedDate { get; set; }       // defaults to UtcNow if null
}

public class UpdateMissionRequest {                  // partial update
    public string? Name { get; set; }
    public Guid?   VehicleId { get; set; }
}

public class GetMissionsQuery {
    public string?   Name { get; set; }              // case-INSENSITIVE contains
    public DateTime? FromDate { get; set; }
    public DateTime? ToDate { get; set; }
    public int       Page { get; set; } = 1;
    public int       PageSize { get; set; } = 20;
    // Results ordered by CreatedDate DESC (newest first).
}

Validation: existence check on VehicleId (returns 400 today via ArgumentException; spec wants 404 — carry-forward divergence). No bounds on Page / PageSize (negative or huge values accepted by binding).

2.3 Waypoint (/missions/{id}/waypoints)

public class GeoPoint {                              // shared value object; all fields nullable
    public decimal? Lat { get; set; }
    public decimal? Lon { get; set; }
    public string?  Mgrs { get; set; }               // Military Grid Reference System
}

public class CreateWaypointRequest {
    public GeoPoint?         GeoPoint { get; set; }  // nullable: all-null is accepted today (no invariant)
    public WaypointSource    WaypointSource { get; set; }    // enum int
    public WaypointObjective WaypointObjective { get; set; } // enum int
    public int               OrderNum { get; set; }
    public decimal           Height { get; set; }
}

public class UpdateWaypointRequest {                 // identical SHAPE to Create -- non-nullable enums/numerics
    public GeoPoint?         GeoPoint { get; set; }
    public WaypointSource    WaypointSource { get; set; }
    public WaypointObjective WaypointObjective { get; set; }
    public int               OrderNum { get; set; }
    public decimal           Height { get; set; }
}

Validation: NONE. No min-length, no enum range check, no Lat/Lon bounds, no MGRS format validation. GeoPoint may be all-null. UpdateWaypoint is structurally NOT partial — every field gets overwritten on PUT (inconsistent with vehicle's partial-update pattern).

Spec divergence (Geopoint): spec stores Waypoints.GPS as a single string GPS field with Lat <-> MGRS auto-conversion (../../suite/_docs/02_missions.md, ../../suite/_docs/00_database_schema.md). Code stores 3 separate columns with NO conversion. Carry-forward.

3. Persisted data — owned tables (post-B7+B9)

FKs in this section are declared as DB-level REFERENCES constraints in DatabaseMigrator.cs, not just logical. Date columns use PostgreSQL TIMESTAMP (no timezone, NOT TIMESTAMPTZ) — DateTime.Kind is normalized to Unspecified on read.

3.1 vehicles (owned)

Column Type Nullable Default Notes
id UUID NO primary key
type INTEGER NO 0 VehicleType enum int (Plane / Copter / UGV / GuidedMissile)
model TEXT NO
name TEXT NO
fuel_type INTEGER NO 0 FuelType enum int
battery_capacity NUMERIC NO 0
engine_consumption NUMERIC NO 0
engine_consumption_idle NUMERIC NO 0
is_default BOOLEAN NO FALSE "exactly one default" enforced by VehicleService (stricter than spec — B12 decision)

3.2 missions (owned)

Column Type Nullable Default Notes
id UUID NO primary key
created_date TIMESTAMP NO NOW() server-assigned UtcNow if not supplied; TIMESTAMP (no timezone)
name TEXT NO
vehicle_id UUID NO REFERENCES vehicles(id) — DB-level FK; PostgreSQL error 23503 raised if parent vehicle was deleted between service-layer existence check and insert

Index: ix_missions_vehicle_id on vehicle_id.

3.3 waypoints (owned)

Column Type Nullable Default Notes
id UUID NO primary key
mission_id UUID NO REFERENCES missions(id) — DB-level FK
lat NUMERIC YES spec divergence — see § 2.3
lon NUMERIC YES spec divergence
mgrs TEXT YES spec divergence
waypoint_source INTEGER NO 0 WaypointSource enum int
waypoint_objective INTEGER NO 0 WaypointObjective enum int
order_num INTEGER NO 0 listing order
height NUMERIC NO 0 metres

Index: ix_waypoints_mission_id on mission_id.

3.4 map_objects (owned schema; written by autopilot)

Column Type Nullable Default Notes
id UUID NO primary key
mission_id UUID NO REFERENCES missions(id) — DB-level FK
h3_index TEXT NO Uber H3 hex grid cell
mgrs TEXT NO
lat NUMERIC YES
lon NUMERIC YES
class_num INTEGER NO 0 detection class id
label TEXT NO ''
size_width_m NUMERIC NO 0
size_length_m NUMERIC NO 0
confidence NUMERIC NO 0 0..1
object_status INTEGER NO 0 ObjectStatus enum int
first_seen_at TIMESTAMP NO NOW() TIMESTAMP (no timezone)
last_seen_at TIMESTAMP NO NOW() TIMESTAMP (no timezone)

Index: ix_map_objects_mission_id on mission_id.

autopilot is the writer (per ../../suite/_docs/06_autopilot_design.md); this service owns the schema and cascade-deletes only.

4. Persisted data — borrowed read-only stubs

Table Schema owner This service uses for
media annotations (per ../../suite/_docs/01_annotations.md) id resolution + cascade-delete walk on mission/waypoint delete
annotations annotations id resolution + cascade-delete walk
detection (singular by upstream owner) Detection pipeline cascade-delete walk

Stub schemas (just enough to query / delete by id):

[Table("media")]       public class Media       { [PrimaryKey, Column("id")] public string Id = ""; [Column("waypoint_id")] public Guid? WaypointId; }
[Table("annotations")] public class Annotation  { [PrimaryKey, Column("id")] public string Id = ""; [Column("media_id")]    public string MediaId = ""; }
[Table("detection")]   public class Detection   { [PrimaryKey, Column("id")] public Guid   Id;     [Column("annotation_id")] public string AnnotationId = ""; }

Migrations for these tables are owned by the respective sibling services. If they have not migrated on a given device, this service's cascade-delete walk fails on relation does not exist (abnormal deployment).

5. Removed in B7 (post-B7+B9 schema)

These tables and entities are out of this repo; cleanup happens once on legacy devices via the B9 DROP TABLE IF EXISTS block in DatabaseMigrator:

Table Pre-B7 owner Post-B7 owner
orthophotos this repo (Orthophoto entity, 03_gps_denied component) gps-denied service (separate repo)
gps_corrections this repo (GpsCorrection entity, 03_gps_denied component) gps-denied service

gps-denied references mission_id / waypoint_id as plain GUIDs in its OWN tables — no runtime coupling, no FK declaration, no cascade by this service.

6. Enum values

Enum Values Persisted as Defined in
VehicleType Plane=0, Copter=1, UGV=2, GuidedMissile=3 INTEGER Enums/VehicleType.cs (post-B6)
FuelType Electric=0, Gasoline=1, Diesel=2 INTEGER Enums/FuelType.cs
WaypointSource Operator=0, Mission=1, ... INTEGER Enums/WaypointSource.cs
WaypointObjective Surveillance=0, Strike=1, ... INTEGER Enums/WaypointObjective.cs
ObjectStatus Active=0, Lost=1, ... INTEGER Enums/ObjectStatus.cs (used only by MapObject)

Per _docs/02_document/modules/enums.md, integer values are NOT range-validated on input — model binding accepts any int.

7. Inbound data shapes (HTTP)

Endpoint Method Body / Query Returns
/vehicles GET ?name=&isDefault= List<Vehicle> (PascalCase JSON; not paginated; ordered by Name ASC; name filter is case-INSENSITIVE)
/vehicles/{id} GET Vehicle
/vehicles POST CreateVehicleRequest Vehicle (created)
/vehicles/{id} PUT UpdateVehicleRequest (partial) Vehicle (updated)
/vehicles/{id}/setDefault POST SetDefaultRequest Vehicle
/vehicles/{id} DELETE 204 / 409 if referenced
/missions GET ?name=&fromDate=&toDate=&page=&pageSize= PaginatedResponse<Mission> (ordered by CreatedDate DESC; name filter case-INSENSITIVE)
/missions/{id} GET Mission
/missions POST CreateMissionRequest Mission (created)
/missions/{id} PUT UpdateMissionRequest (partial) Mission (updated)
/missions/{id} DELETE 204 / 404; runs F3 cascade
/missions/{id}/waypoints GET List<Waypoint> (unpaginated, ordered by OrderNum)
/missions/{id}/waypoints POST CreateWaypointRequest Waypoint (created)
/missions/{id}/waypoints/{wpId} PUT UpdateWaypointRequest (full overwrite) Waypoint
/missions/{id}/waypoints/{wpId} DELETE 204; runs F4 scoped cascade
/health GET — anonymous 200 { "status": "healthy" }

All routes except /health require JWT bearer with permissions=FL claim.