Files
Oleksandr Bezdieniezhnykh 7025f4d075 refactor: enhance JWT authentication and CORS configuration
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.
2026-05-14 19:48:25 +03:00

159 lines
7.3 KiB
Markdown

# Module: `Azaion.Missions.DTOs`
**Files (15)** -- grouped by concern:
| Group | Files |
|-------|-------|
| Shared value objects | `GeoPoint.cs` |
| Shared response wrappers | `PaginatedResponse.cs`, `ErrorResponse.cs` |
| Vehicle requests/queries | `CreateVehicleRequest.cs`, `UpdateVehicleRequest.cs`, `GetVehiclesQuery.cs`, `SetDefaultRequest.cs` |
| Mission requests/queries | `CreateMissionRequest.cs`, `UpdateMissionRequest.cs`, `GetMissionsQuery.cs` |
| Waypoint requests | `CreateWaypointRequest.cs`, `UpdateWaypointRequest.cs` |
> **NOTE (forward-looking)**: post-rename file names. Today's source has `CreateAircraftRequest.cs` / `CreateFlightRequest.cs` / etc. Renames tracked under Jira AZ-EPIC child B6.
## Purpose
HTTP request/response/query payloads for the controller layer. Plain POCOs with public mutable properties -- no validation attributes, no record types.
## Public Interface
### Shared
```csharp
public class GeoPoint {
public decimal? Lat { get; set; }
public decimal? Lon { get; set; }
public string? Mgrs { get; set; } // Military Grid Reference System
}
public class PaginatedResponse<T> {
public List<T> Items { get; set; } = [];
public int TotalCount { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
public class ErrorResponse {
public int StatusCode { get; set; }
public string Message { get; set; } = string.Empty;
public List<string>? Errors { get; set; }
}
```
### Vehicle
```csharp
public class CreateVehicleRequest {
public VehicleType Type { get; set; } // Plane | Copter | UGV | GuidedMissile
public string Model { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public FuelType FuelType { get; set; }
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 semantics, applied per non-null field
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; }
public bool? IsDefault { get; set; }
}
public class SetDefaultRequest {
public bool IsDefault { get; set; }
}
```
### Mission
```csharp
public class CreateMissionRequest {
public Guid VehicleId { get; set; }
public string Name { get; set; } = string.Empty;
public DateTime? CreatedDate { get; set; } // Defaults to UtcNow if null
}
public class UpdateMissionRequest {
public string? Name { get; set; }
public Guid? VehicleId { get; set; }
}
public class GetMissionsQuery {
public string? Name { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 20;
}
```
### Waypoint
```csharp
public class CreateWaypointRequest {
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; }
}
public class UpdateWaypointRequest { // identical shape to Create
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; }
}
```
## Internal Logic
Pure data containers. No methods, no constructors beyond the implicit default.
## Dependencies
- `Azaion.Missions.Enums` -- for typed enum properties
## Consumers
- `Services.VehicleService`, `Services.MissionService`, `Services.WaypointService` -- request DTOs as method parameters; `PaginatedResponse<Mission>` returned from `MissionService.GetMissions`.
- `Controllers.VehiclesController`, `Controllers.MissionsController` -- `[FromBody]` / `[FromQuery]` binding.
## Data Models
Mirror the corresponding entity columns minus identity/timestamps (those are server-assigned).
## Configuration / External Integrations / Security
None directly -- all binding is provided by ASP.NET Core model binding with no custom validators or `[Required]` / `[Range]` attributes.
## Tests
None present.
## Notes / Smells
- **No validation**: nothing prevents `CreateVehicleRequest.Name = ""`, negative `BatteryCapacity`, `OrderNum < 0`, or out-of-range enum values (binding on int will accept any int and persist it). Carry to AC / restrictions in Step 6.
- **`UpdateWaypointRequest` is structurally identical to `CreateWaypointRequest`** but uses non-nullable enum/numeric fields, meaning every `PUT` overwrites all fields -- no partial-update semantics for waypoints (unlike vehicle, which uses `?` everywhere). Inconsistency between resources.
- **`PaginatedResponse<T>`** is only used for `Mission` listing; `GetVehicles` returns a plain `List<Vehicle>` (no pagination, no total count), even though `GetVehiclesQuery` exists. Inconsistent listing contract -- matches spec (vehicles are a small dataset).
- **`ErrorResponse`** is defined but not used by `ErrorHandlingMiddleware` (which writes an anonymous object literal). Dead code candidate.
- `GeoPoint` allows all-null (Lat, Lon, Mgrs all optional). No invariant ensures at least one location representation is populated.
- **Spec divergence (Geopoint)**: `../../suite/_docs/02_missions.md` and `../../suite/_docs/00_database_schema.md` define `Waypoints.GPS` as a single `string GPS` field with auto-conversion (`Lat <-> MGRS`). Code stores three separate columns (`lat NUMERIC`, `lon NUMERIC`, `mgrs TEXT`) and exposes them as three flat properties without conversion logic. Carry to verification log.
- **Spec divergence (ErrorResponse)**: spec `errors` is `object?` keyed by field name (per-field validation arrays). Code defines `List<string>? Errors` and the type is unused on the wire (middleware emits an anonymous object instead). Carry to verification log.
- **Spec divergence (PaginatedResponse case-style)**: suite-wide standard is camelCase (`items`, `totalCount`, `page`, `pageSize`). `PaginatedResponse<T>` declares PascalCase properties and System.Text.Json's defaults preserve them, so on-the-wire output is `{"Items":..., "TotalCount":..., ...}`. No `JsonNamingPolicy.CamelCase` is configured.
- **Spec partial conformance (ErrorResponse / error envelope)**: the static `ErrorResponse` DTO is **unused on the wire** -- `Middleware.ErrorHandlingMiddleware` writes an anonymous object literal instead. That anonymous object happens to use lowercase property names (`statusCode`, `message`), which `System.Text.Json` preserves, so the live error envelope IS camelCase (matching spec on case) but still missing the spec's `errors: object?` field. Were `ErrorResponse` ever used directly it would emit PascalCase (`StatusCode`, `Message`, `Errors`) and additionally have the wrong `Errors` shape (`List<string>?` vs spec's `object?`). Two carry-forward concerns: add the `errors` field to the live envelope, and either remove the dead `ErrorResponse` DTO or fix it to match spec.