Skip to content

Commit 44a5be2

Browse files
committed
web: extend api to list standard build files from firmware server
1 parent e9021a2 commit 44a5be2

5 files changed

Lines changed: 111 additions & 0 deletions

File tree

web/api/v1/vehicles.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
VehicleBase,
66
VersionOut,
77
BoardOut,
8+
StandardArtifactOut,
89
FeatureOut,
910
)
1011
from web.services.vehicles import get_vehicles_service, VehiclesService
12+
from metadata_manager.ap_src_meta_fetcher import FirmwareServerUnavailableError
1113

1214
router = APIRouter(prefix="/vehicles", tags=["vehicles"])
1315

@@ -183,6 +185,55 @@ async def get_board(
183185
return board
184186

185187

188+
@router.get(
189+
"/{vehicle_id}/versions/{version_id}/boards/{board_id}/standard_artifacts",
190+
response_model=List[StandardArtifactOut],
191+
responses={
192+
404: {"description": "Standard artifacts not found"},
193+
502: {"description": "Firmware server unavailable"},
194+
}
195+
)
196+
async def list_board_standard_artifacts(
197+
vehicle_id: str = Path(..., description="Vehicle identifier"),
198+
version_id: str = Path(..., description="Version identifier"),
199+
board_id: str = Path(..., description="Board identifier"),
200+
service: VehiclesService = Depends(get_vehicles_service)
201+
):
202+
"""
203+
Get standard build artifact files from firmware.ardupilot.org for a board.
204+
205+
Args:
206+
vehicle_id: The vehicle identifier
207+
version_id: The version identifier
208+
board_id: The board identifier
209+
210+
Returns:
211+
List of artifact files with download URLs
212+
"""
213+
try:
214+
artifacts = service.get_board_standard_artifacts(
215+
vehicle_id, version_id, board_id
216+
)
217+
except FirmwareServerUnavailableError:
218+
raise HTTPException(
219+
status_code=502,
220+
detail=(
221+
"Failed to fetch standard artifacts from firmware server"
222+
)
223+
)
224+
225+
if artifacts is None:
226+
raise HTTPException(
227+
status_code=404,
228+
detail=(
229+
f"Standard artifacts not found for board '{board_id}' "
230+
f"in vehicle '{vehicle_id}' version '{version_id}'"
231+
)
232+
)
233+
234+
return artifacts
235+
236+
186237
# --- Feature Endpoints ---
187238
@router.get(
188239
"/{vehicle_id}/versions/{version_id}/boards/{board_id}/features",

web/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ uvicorn==0.40.0
33
pydantic==2.5.0
44
redis==5.2.1
55
requests==2.31.0
6+
beautifulsoup4==4.12.3
67
jsonschema==4.20.0
78
dill==0.3.8
89
packaging==25.0

web/schemas/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
VersionOut,
2828
BoardBase,
2929
BoardOut,
30+
StandardArtifactOut,
3031
CategoryBase,
3132
FeatureDefault,
3233
FeatureBase,
@@ -49,6 +50,7 @@
4950
"VersionOut",
5051
"BoardBase",
5152
"BoardOut",
53+
"StandardArtifactOut",
5254
"CategoryBase",
5355
"FeatureDefault",
5456
"FeatureBase",

web/schemas/vehicles.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# app/schemas/vehicles.py
2+
from datetime import datetime
23
from typing import Literal, Optional
34

45
from pydantic import BaseModel, Field
@@ -49,6 +50,15 @@ class BoardOut(BoardBase):
4950
version_id: str = Field(..., description="Associated version identifier")
5051

5152

53+
class StandardArtifactOut(BaseModel):
54+
name: str = Field(..., description="Artifact filename")
55+
url: str = Field(..., description="Download URL on firmware.ardupilot.org")
56+
size: Optional[int] = Field(None, description="File size in bytes")
57+
modified: Optional[datetime] = Field(
58+
None, description="Last modified time (UTC, ISO 8601)"
59+
)
60+
61+
5262
# --- Features ---
5363
class CategoryBase(BaseModel):
5464
id: str = Field(..., description="Unique category identifier")

web/services/vehicles.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
RemoteInfo,
1111
VersionOut,
1212
BoardOut,
13+
StandardArtifactOut,
1314
FeatureOut,
1415
CategoryBase,
1516
FeatureDefault,
@@ -150,6 +151,52 @@ def get_board(
150151
return board
151152
return None
152153

154+
def get_board_standard_artifacts(
155+
self,
156+
vehicle_id: str,
157+
version_id: str,
158+
board_id: str,
159+
) -> Optional[List[StandardArtifactOut]]:
160+
"""Get standard build artifacts from firmware.ardupilot.org for a board."""
161+
version_info = self.versions_fetcher.get_version_info(
162+
vehicle_id=vehicle_id,
163+
version_id=version_id,
164+
)
165+
if not version_info:
166+
return None
167+
168+
if not self.get_board(vehicle_id, version_id, board_id):
169+
return None
170+
171+
if version_info.ap_build_artifacts_url is None:
172+
return None
173+
174+
logger.info(
175+
f'Standard artifacts requested for {vehicle_id} '
176+
f'{version_info.remote_info.name} {version_info.commit_ref} '
177+
f'board {board_id}'
178+
)
179+
180+
artifacts = (
181+
self.ap_src_metadata_fetcher.get_board_standard_artifacts_from_fw_server(
182+
version_artifacts_url=version_info.ap_build_artifacts_url,
183+
board_id=board_id,
184+
vehicle_id=vehicle_id,
185+
)
186+
)
187+
if artifacts is None:
188+
return None
189+
190+
return [
191+
StandardArtifactOut(
192+
name=entry["name"],
193+
url=entry["url"],
194+
size=entry.get("size"),
195+
modified=entry.get("modified"),
196+
)
197+
for entry in artifacts
198+
]
199+
153200
def get_features(
154201
self,
155202
vehicle_id: str,

0 commit comments

Comments
 (0)