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

4.3 KiB

Module: Azaion.Missions.Services.WaypointService

File: Services/WaypointService.cs

NOTE (forward-looking): post-rename + post-GPS-Denied-removal. Today's source still uses Flight/flightId and the cascade reaches into gps_corrections. Renames tracked under Jira AZ-EPIC child B6; cascade shrink under B7.

Purpose

CRUD over waypoints (sub-aggregate of Mission) plus a manual cascade-delete chain that walks waypoint -> media -> annotations -> detection. All operations are scoped by missionId -- no cross-mission waypoint addressing.

Public Interface

public class WaypointService(AppDataConnection db) {
    Task<Waypoint>        CreateWaypoint(Guid missionId, CreateWaypointRequest request);
    Task<Waypoint>        UpdateWaypoint(Guid missionId, Guid waypointId, UpdateWaypointRequest request);
    Task<List<Waypoint>>  GetWaypoints(Guid missionId);
    Task                  DeleteWaypoint(Guid missionId, Guid waypointId);
}

Internal Logic

CreateWaypoint

  1. Existence check: db.Missions.AnyAsync(m => m.Id == missionId) -> KeyNotFoundException ("Mission not found") on miss -> 404.
  2. Build a fresh Waypoint:
    • Id = Guid.NewGuid(), MissionId = missionId.
    • Lat, Lon, Mgrs from request.GeoPoint? (all three null-passthrough; if GeoPoint is null, all three are null).
    • WaypointSource, WaypointObjective copied as-is.
    • OrderNum, Height copied as-is.
  3. db.InsertAsync(waypoint).

UpdateWaypoint

  1. Load by composite predicate w.MissionId == missionId && w.Id == waypointId -> 404 on miss (so updates with the wrong missionId get 404, not 403).
  2. Full overwrite -- every field is set unconditionally from the request, including null-passthrough on Lat/Lon/Mgrs. There is no partial-update semantic here, unlike UpdateVehicleRequest.
  3. db.UpdateAsync(waypoint).

GetWaypoints

  • db.Waypoints.Where(w.MissionId == missionId).OrderBy(w.OrderNum).ToListAsync() -- no pagination.

DeleteWaypoint -- manual cascade

  1. Load by composite (404 on miss).
  2. mediaIds = SELECT id FROM media WHERE waypoint_id = waypointId.
  3. If any media exist:
    • annotationIds = SELECT id FROM annotations WHERE media_id IN (mediaIds).
    • If any annotations exist: DELETE FROM detection WHERE annotation_id IN (annotationIds).
    • DELETE FROM annotations WHERE media_id IN (mediaIds).
  4. DELETE FROM media WHERE waypoint_id = waypointId.
  5. DELETE FROM waypoints WHERE id = waypointId.

Removed from cascade in B7: gps_corrections WHERE waypoint_id = waypointId. Schema gone after B9.

No transaction wraps the cascade either.

Dependencies

  • AppDataConnection, entities Waypoint, Mission (existence check), and 3 dependent entities used in the cascade.
  • DTOs.GeoPoint, DTOs.CreateWaypointRequest, DTOs.UpdateWaypointRequest.
  • Azaion.Missions.Enums (WaypointSource, WaypointObjective -- typed properties).

Consumers

  • Controllers.MissionsController -- exposed under missions/{id}/waypoints/* routes.

Data Models

Reads missions (existence). Writes waypoints. Cascade also writes media, annotations, detection.

Configuration / External Integrations

None.

Security

  • Behind [Authorize(Policy = "FL")] via the controller.
  • Composite-key load (MissionId AND Id) means a user cannot operate on a waypoint by guessing only its id -- they must also know the mission id (defense in depth, though both are GUIDs).

Tests

None present.

Notes / Smells

  1. UpdateWaypoint does a full overwrite even though UpdateWaypointRequest is "partial-shaped". Any client sending {} would silently zero out Lat/Lon/Mgrs/OrderNum/Height and reset enums to 0. Client contract is fragile.
  2. waypoint_id not found vs. mission_id/waypoint_id mismatch both return 404 with the same message -- slight UX issue (you can't tell which id is wrong).
  3. No reorder endpoint -- OrderNum is set in the request but there's no atomic "reorder this list" operation. Reordering N waypoints requires N PUTs and is racy.
  4. Same transaction gap as MissionService.DeleteMission -- no BeginTransactionAsync around the cascade.
  5. GeoPoint? swallowing -- sending a request with GeoPoint = null clears the location entirely. Likely intentional but combined with "no validation" means an empty waypoint can be created.