diff --git a/game/server/combat/models.py b/game/server/combat/models.py index 092a4e21..f579743c 100644 --- a/game/server/combat/models.py +++ b/game/server/combat/models.py @@ -18,6 +18,9 @@ class FrozenCombatJs(BaseModel): target_positions: list[LeafletPoint] | None footprint: list[LeafletPoly] | None + class Config: + title = "FrozenCombat" + @staticmethod def for_combat(combat: FrozenCombat, theater: ConflictTheater) -> FrozenCombatJs: if isinstance(combat, AirCombat): diff --git a/game/server/controlpoints/models.py b/game/server/controlpoints/models.py index 3bd5875c..b21314b6 100644 --- a/game/server/controlpoints/models.py +++ b/game/server/controlpoints/models.py @@ -20,6 +20,9 @@ class ControlPointJs(BaseModel): destination: LeafletPoint | None sidc: str + class Config: + title = "ControlPoint" + @staticmethod def for_control_point(control_point: ControlPoint) -> ControlPointJs: destination = None diff --git a/game/server/controlpoints/routes.py b/game/server/controlpoints/routes.py index 3bd9850b..0e644b3f 100644 --- a/game/server/controlpoints/routes.py +++ b/game/server/controlpoints/routes.py @@ -1,6 +1,6 @@ from dcs import Point from dcs.mapping import LatLng -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Body, Depends, HTTPException, status from game import Game from .models import ControlPointJs @@ -12,14 +12,18 @@ from ...sim import GameUpdateEvents router: APIRouter = APIRouter(prefix="/control-points") -@router.get("/") +@router.get( + "/", operation_id="list_control_points", response_model=list[ControlPointJs] +) def list_control_points( game: Game = Depends(GameContext.require), ) -> list[ControlPointJs]: return ControlPointJs.all_in_game(game) -@router.get("/{cp_id}") +@router.get( + "/{cp_id}", operation_id="get_control_point_by_id", response_model=ControlPointJs +) def get_control_point( cp_id: int, game: Game = Depends(GameContext.require) ) -> ControlPointJs: @@ -32,7 +36,11 @@ def get_control_point( return ControlPointJs.for_control_point(cp) -@router.get("/{cp_id}/destination-in-range") +@router.get( + "/{cp_id}/destination-in-range", + operation_id="control_point_destination_in_range", + response_model=bool, +) def destination_in_range( cp_id: int, lat: float, lng: float, game: Game = Depends(GameContext.require) ) -> bool: @@ -47,9 +55,15 @@ def destination_in_range( return cp.destination_in_range(point) -@router.put("/{cp_id}/destination") +@router.put( + "/{cp_id}/destination", + operation_id="set_control_point_destination", + status_code=status.HTTP_204_NO_CONTENT, +) def set_destination( - cp_id: int, destination: LeafletPoint, game: Game = Depends(GameContext.require) + cp_id: int, + destination: LeafletPoint = Body(..., title="destination"), + game: Game = Depends(GameContext.require), ) -> None: cp = game.theater.find_control_point_by_id(cp_id) if cp is None: @@ -77,7 +91,11 @@ def set_destination( EventStream.put_nowait(GameUpdateEvents().update_control_point(cp)) -@router.put("/{cp_id}/cancel-travel") +@router.put( + "/{cp_id}/cancel-travel", + operation_id="clear_control_point_destination", + status_code=status.HTTP_204_NO_CONTENT, +) def cancel_travel(cp_id: int, game: Game = Depends(GameContext.require)) -> None: cp = game.theater.find_control_point_by_id(cp_id) if cp is None: diff --git a/game/server/debuggeometries/models.py b/game/server/debuggeometries/models.py index 7ee33322..3fd3348e 100644 --- a/game/server/debuggeometries/models.py +++ b/game/server/debuggeometries/models.py @@ -16,6 +16,9 @@ class HoldZonesJs(BaseModel): permissible_zones: list[LeafletPoly] = Field(alias="permissibleZones") preferred_lines: list[LeafletPoly] = Field(alias="preferredLines") + class Config: + title = "HoldZones" + @classmethod def empty(cls) -> HoldZonesJs: return HoldZonesJs( @@ -62,6 +65,9 @@ class IpZonesJs(BaseModel): permissibleZone: LeafletPoly = Field(alias="permissibleZone") safeZones: list[LeafletPoly] = Field(alias="safeZones") + class Config: + title = "IpZones" + @classmethod def empty(cls) -> IpZonesJs: return IpZonesJs(homeBubble=[], ipBubble=[], permissibleZone=[], safeZones=[]) @@ -89,6 +95,9 @@ class JoinZonesJs(BaseModel): permissible_zones: list[LeafletPoly] = Field(alias="permissibleZones") preferred_lines: list[LeafletPoly] = Field(alias="preferredLines") + class Config: + title = "JoinZones" + @classmethod def empty(cls) -> JoinZonesJs: return JoinZonesJs( diff --git a/game/server/debuggeometries/routes.py b/game/server/debuggeometries/routes.py index 3b6f50e4..b85b4250 100644 --- a/game/server/debuggeometries/routes.py +++ b/game/server/debuggeometries/routes.py @@ -9,19 +9,25 @@ from .models import HoldZonesJs, IpZonesJs, JoinZonesJs router: APIRouter = APIRouter(prefix="/debug/waypoint-geometries") -@router.get("/hold/{flight_id}") +@router.get( + "/hold/{flight_id}", operation_id="get_debug_hold_zones", response_model=HoldZonesJs +) def hold_zones( flight_id: UUID, game: Game = Depends(GameContext.require) ) -> HoldZonesJs: return HoldZonesJs.for_flight(game.db.flights.get(flight_id), game) -@router.get("/ip/{flight_id}") +@router.get( + "/ip/{flight_id}", operation_id="get_debug_ip_zones", response_model=IpZonesJs +) def ip_zones(flight_id: UUID, game: Game = Depends(GameContext.require)) -> IpZonesJs: return IpZonesJs.for_flight(game.db.flights.get(flight_id), game) -@router.get("/join/{flight_id}") +@router.get( + "/join/{flight_id}", operation_id="get_debug_join_zones", response_model=JoinZonesJs +) def join_zones( flight_id: UUID, game: Game = Depends(GameContext.require) ) -> JoinZonesJs: diff --git a/game/server/flights/models.py b/game/server/flights/models.py index f306a2c0..cb337603 100644 --- a/game/server/flights/models.py +++ b/game/server/flights/models.py @@ -22,6 +22,9 @@ class FlightJs(BaseModel): sidc: str waypoints: list[FlightWaypointJs] | None + class Config: + title = "Flight" + @staticmethod def for_flight(flight: Flight, with_waypoints: bool) -> FlightJs: # Don't provide a location for aircraft that aren't in the air. Later we can diff --git a/game/server/flights/routes.py b/game/server/flights/routes.py index d66d60a1..7ffe1680 100644 --- a/game/server/flights/routes.py +++ b/game/server/flights/routes.py @@ -12,14 +12,14 @@ from game.server.leaflet import LeafletPoly, ShapelyUtil router: APIRouter = APIRouter(prefix="/flights") -@router.get("/") +@router.get("/", operation_id="list_flights", response_model=list[FlightJs]) def list_flights( with_waypoints: bool = False, game: Game = Depends(GameContext.require) ) -> list[FlightJs]: return FlightJs.all_in_game(game, with_waypoints) -@router.get("/{flight_id}") +@router.get("/{flight_id}", operation_id="get_flight_by_id", response_model=FlightJs) def get_flight( flight_id: UUID, with_waypoints: bool = False, @@ -29,7 +29,11 @@ def get_flight( return FlightJs.for_flight(flight, with_waypoints) -@router.get("/{flight_id}/commit-boundary") +@router.get( + "/{flight_id}/commit-boundary", + operation_id="get_commit_boundary_for_flight", + response_model=LeafletPoly, +) def commit_boundary( flight_id: UUID, game: Game = Depends(GameContext.require) ) -> LeafletPoly: diff --git a/game/server/frontlines/models.py b/game/server/frontlines/models.py index 1eab8d13..7d6c3292 100644 --- a/game/server/frontlines/models.py +++ b/game/server/frontlines/models.py @@ -17,6 +17,9 @@ class FrontLineJs(BaseModel): id: UUID extents: list[LeafletPoint] + class Config: + title = "FrontLine" + @staticmethod def for_front_line(front_line: FrontLine) -> FrontLineJs: a = front_line.position.point_from_heading( diff --git a/game/server/frontlines/routes.py b/game/server/frontlines/routes.py index ec217c5f..c62433d0 100644 --- a/game/server/frontlines/routes.py +++ b/game/server/frontlines/routes.py @@ -9,12 +9,14 @@ from ..dependencies import GameContext router: APIRouter = APIRouter(prefix="/front-lines") -@router.get("/") +@router.get("/", operation_id="list_front_lines", response_model=list[FrontLineJs]) def list_front_lines(game: Game = Depends(GameContext.require)) -> list[FrontLineJs]: return FrontLineJs.all_in_game(game) -@router.get("/{front_line_id}") +@router.get( + "/{front_line_id}", operation_id="get_front_line_by_id", response_model=FrontLineJs +) def get_front_line( front_line_id: UUID, game: Game = Depends(GameContext.require) ) -> FrontLineJs: diff --git a/game/server/game/models.py b/game/server/game/models.py index 80ce07de..e6029d5d 100644 --- a/game/server/game/models.py +++ b/game/server/game/models.py @@ -23,6 +23,9 @@ class GameJs(BaseModel): flights: list[FlightJs] map_center: LeafletPoint + class Config: + title = "Game" + @staticmethod def from_game(game: Game) -> GameJs: return GameJs( diff --git a/game/server/game/routes.py b/game/server/game/routes.py index dad7d7ba..f960d485 100644 --- a/game/server/game/routes.py +++ b/game/server/game/routes.py @@ -7,7 +7,7 @@ from .models import GameJs router: APIRouter = APIRouter(prefix="/game") -@router.get("/") +@router.get("/", operation_id="get_game_state", response_model=GameJs) def game_state(game: Game | None = Depends(GameContext.get)) -> GameJs | None: if game is None: return None diff --git a/game/server/leaflet.py b/game/server/leaflet.py index 9ec58543..dcc25ffd 100644 --- a/game/server/leaflet.py +++ b/game/server/leaflet.py @@ -19,6 +19,8 @@ class LeafletPoint(BaseModel): class Config: orm_mode = True + title = "LatLng" + class ShapelyUtil: @staticmethod diff --git a/game/server/mapzones/models.py b/game/server/mapzones/models.py index 25789305..329ab6db 100644 --- a/game/server/mapzones/models.py +++ b/game/server/mapzones/models.py @@ -12,11 +12,17 @@ class MapZonesJs(BaseModel): exclusion: list[LeafletPoly] sea: list[LeafletPoly] + class Config: + title = "MapZones" + class UnculledZoneJs(BaseModel): position: LeafletPoint radius: float + class Config: + title = "UnculledZone" + class ThreatZonesJs(BaseModel): full: list[LeafletPoly] @@ -24,6 +30,9 @@ class ThreatZonesJs(BaseModel): air_defenses: list[LeafletPoly] radar_sams: list[LeafletPoly] + class Config: + title = "ThreatZones" + @classmethod def from_zones(cls, zones: ThreatZones, theater: ConflictTheater) -> ThreatZonesJs: return ThreatZonesJs( @@ -37,3 +46,6 @@ class ThreatZonesJs(BaseModel): class ThreatZoneContainerJs(BaseModel): blue: ThreatZonesJs red: ThreatZonesJs + + class Config: + title = "ThreatZoneContainer" diff --git a/game/server/mapzones/routes.py b/game/server/mapzones/routes.py index 54ca8d99..8b64bc97 100644 --- a/game/server/mapzones/routes.py +++ b/game/server/mapzones/routes.py @@ -8,7 +8,7 @@ from ..leaflet import ShapelyUtil router: APIRouter = APIRouter(prefix="/map-zones") -@router.get("/terrain") +@router.get("/terrain", operation_id="get_terrain_zones", response_model=MapZonesJs) def get_terrain(game: Game = Depends(GameContext.require)) -> MapZonesJs: zones = game.theater.landmap if zones is None: @@ -21,7 +21,9 @@ def get_terrain(game: Game = Depends(GameContext.require)) -> MapZonesJs: ) -@router.get("/unculled") +@router.get( + "/unculled", operation_id="list_unculled_zones", response_model=list[UnculledZoneJs] +) def get_unculled_zones( game: Game = Depends(GameContext.require), ) -> list[UnculledZoneJs]: @@ -33,7 +35,9 @@ def get_unculled_zones( ] -@router.get("/threats") +@router.get( + "/threats", operation_id="get_threat_zones", response_model=ThreatZoneContainerJs +) def get_threat_zones( game: Game = Depends(GameContext.require), ) -> ThreatZoneContainerJs: diff --git a/game/server/navmesh/models.py b/game/server/navmesh/models.py index 7bfe7199..672f153e 100644 --- a/game/server/navmesh/models.py +++ b/game/server/navmesh/models.py @@ -8,3 +8,6 @@ from game.server.leaflet import LeafletPoly class NavMeshPolyJs(BaseModel): poly: LeafletPoly threatened: bool + + class Config: + title = "NavMeshPoly" diff --git a/game/server/navmesh/routes.py b/game/server/navmesh/routes.py index ffd76c7d..b7858612 100644 --- a/game/server/navmesh/routes.py +++ b/game/server/navmesh/routes.py @@ -8,7 +8,7 @@ from ..leaflet import ShapelyUtil router: APIRouter = APIRouter(prefix="/navmesh") -@router.get("/", response_model=list[NavMeshPolyJs]) +@router.get("/", operation_id="get_navmesh", response_model=list[NavMeshPolyJs]) def get( for_player: bool, game: Game = Depends(GameContext.require) ) -> list[NavMeshPolyJs]: diff --git a/game/server/qt/routes.py b/game/server/qt/routes.py index ae6e1ff9..ac08b261 100644 --- a/game/server/qt/routes.py +++ b/game/server/qt/routes.py @@ -8,7 +8,11 @@ from ..dependencies import GameContext, QtCallbacks, QtContext router: APIRouter = APIRouter(prefix="/qt") -@router.post("/create-package/front-line/{front_line_id}") +@router.post( + "/create-package/front-line/{front_line_id}", + operation_id="open_new_front_line_package_dialog", + status_code=status.HTTP_204_NO_CONTENT, +) def new_front_line_package( front_line_id: UUID, game: Game = Depends(GameContext.require), @@ -17,7 +21,11 @@ def new_front_line_package( qt.create_new_package(game.db.front_lines.get(front_line_id)) -@router.post("/create-package/tgo/{tgo_id}") +@router.post( + "/create-package/tgo/{tgo_id}", + operation_id="open_new_tgo_package_dialog", + status_code=status.HTTP_204_NO_CONTENT, +) def new_tgo_package( tgo_id: UUID, game: Game = Depends(GameContext.require), @@ -26,7 +34,11 @@ def new_tgo_package( qt.create_new_package(game.db.tgos.get(tgo_id)) -@router.post("/info/tgo/{tgo_id}") +@router.post( + "/info/tgo/{tgo_id}", + operation_id="open_tgo_info_dialog", + status_code=status.HTTP_204_NO_CONTENT, +) def show_tgo_info( tgo_id: UUID, game: Game = Depends(GameContext.require), @@ -35,7 +47,11 @@ def show_tgo_info( qt.show_tgo_info(game.db.tgos.get(tgo_id)) -@router.post("/create-package/control-point/{cp_id}") +@router.post( + "/create-package/control-point/{cp_id}", + operation_id="open_new_control_point_package_dialog", + status_code=status.HTTP_204_NO_CONTENT, +) def new_cp_package( cp_id: int, game: Game = Depends(GameContext.require), @@ -50,7 +66,11 @@ def new_cp_package( qt.create_new_package(cp) -@router.post("/info/control-point/{cp_id}") +@router.post( + "/info/control-point/{cp_id}", + operation_id="open_control_point_info_dialog", + status_code=status.HTTP_204_NO_CONTENT, +) def show_control_point_info( cp_id: int, game: Game = Depends(GameContext.require), diff --git a/game/server/supplyroutes/models.py b/game/server/supplyroutes/models.py index 66bf349a..98bcc675 100644 --- a/game/server/supplyroutes/models.py +++ b/game/server/supplyroutes/models.py @@ -68,6 +68,9 @@ class SupplyRouteJs(BaseModel): blue: bool active_transports: list[str] + class Config: + title = "SupplyRoute" + @staticmethod def for_link( game: Game, a: ControlPoint, b: ControlPoint, points: list[Point], sea: bool diff --git a/game/server/supplyroutes/routes.py b/game/server/supplyroutes/routes.py index 3475fbd5..675b5ba5 100644 --- a/game/server/supplyroutes/routes.py +++ b/game/server/supplyroutes/routes.py @@ -7,7 +7,7 @@ from ..dependencies import GameContext router: APIRouter = APIRouter(prefix="/supply-routes") -@router.get("/") +@router.get("/", operation_id="list_supply_routes", response_model=list[SupplyRouteJs]) def list_supply_routes( game: Game = Depends(GameContext.require), ) -> list[SupplyRouteJs]: diff --git a/game/server/tgos/models.py b/game/server/tgos/models.py index a6e797c4..1133fd39 100644 --- a/game/server/tgos/models.py +++ b/game/server/tgos/models.py @@ -25,6 +25,9 @@ class TgoJs(BaseModel): dead: bool # TODO: Event stream sidc: str # TODO: Event stream + class Config: + title = "Tgo" + @staticmethod def for_tgo(tgo: TheaterGroundObject) -> TgoJs: if not tgo.might_have_aa: diff --git a/game/server/tgos/routes.py b/game/server/tgos/routes.py index 4653bf58..cc84dc22 100644 --- a/game/server/tgos/routes.py +++ b/game/server/tgos/routes.py @@ -9,11 +9,11 @@ from ..dependencies import GameContext router: APIRouter = APIRouter(prefix="/tgos") -@router.get("/") +@router.get("/", operation_id="list_tgos", response_model=list[TgoJs]) def list_tgos(game: Game = Depends(GameContext.require)) -> list[TgoJs]: return TgoJs.all_in_game(game) -@router.get("/{tgo_id}") +@router.get("/{tgo_id}", operation_id="get_tgo_by_id", response_model=TgoJs) def get_tgo(tgo_id: UUID, game: Game = Depends(GameContext.require)) -> TgoJs: return TgoJs.for_tgo(game.db.tgos.get(tgo_id)) diff --git a/game/server/waypoints/models.py b/game/server/waypoints/models.py index 00b140d3..f01e6016 100644 --- a/game/server/waypoints/models.py +++ b/game/server/waypoints/models.py @@ -34,6 +34,9 @@ class FlightWaypointJs(BaseModel): include_in_path: bool timing: str + class Config: + title = "Waypoint" + @staticmethod def for_waypoint( waypoint: FlightWaypoint, flight: Flight, waypoint_idx: int diff --git a/game/server/waypoints/routes.py b/game/server/waypoints/routes.py index 9f51f468..506cbd48 100644 --- a/game/server/waypoints/routes.py +++ b/game/server/waypoints/routes.py @@ -34,14 +34,22 @@ def waypoints_for_flight(flight: Flight) -> list[FlightWaypointJs]: ] -@router.get("/{flight_id}", response_model=list[FlightWaypointJs]) +@router.get( + "/{flight_id}", + operation_id="list_all_waypoints_for_flight", + response_model=list[FlightWaypointJs], +) def all_waypoints_for_flight( flight_id: UUID, game: Game = Depends(GameContext.require) ) -> list[FlightWaypointJs]: return waypoints_for_flight(game.db.flights.get(flight_id)) -@router.post("/{flight_id}/{waypoint_idx}/position") +@router.post( + "/{flight_id}/{waypoint_idx}/position", + operation_id="set_waypoint_position", + status_code=status.HTTP_204_NO_CONTENT, +) def set_position( flight_id: UUID, waypoint_idx: int,