Files
missions/_docs/02_document/modules/dtos.md
T
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

7.3 KiB

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

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

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

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

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.