Refactor API Docs CI workflow to correct ESLint command syntax and enhance Python formatting in CI pipelines; update progress tracking documentation
Some checks failed
API Docs (Node.js Express) / test (16) (push) Failing after 5m29s
API Docs (Node.js Express) / test (18) (push) Failing after 5m25s
API Docs (Node.js Express) / test (20) (push) Failing after 1m4s
API Docs (Node.js Express) / build (push) Has been skipped
API Docs (Node.js Express) / security (push) Has been skipped
LabFusion CI/CD Pipeline / api-gateway (push) Failing after 4m52s
LabFusion CI/CD Pipeline / service-adapters (push) Failing after 5m1s
LabFusion CI/CD Pipeline / api-docs (push) Failing after 5m12s
LabFusion CI/CD Pipeline / frontend (push) Failing after 6m39s
LabFusion CI/CD Pipeline / integration-tests (push) Has been skipped
LabFusion CI/CD Pipeline / security-scan (push) Has been skipped
Docker Build and Push / build-and-push (push) Failing after 34s
Docker Build and Push / security-scan (push) Has been skipped
Integration Tests / integration-tests (push) Failing after 1m33s
Integration Tests / performance-tests (push) Has been skipped
Service Adapters (Python FastAPI) / test (3.1) (push) Failing after 35s
Service Adapters (Python FastAPI) / test (3.11) (push) Failing after 5m20s
Service Adapters (Python FastAPI) / test (3.12) (push) Failing after 5m27s
Service Adapters (Python FastAPI) / test (3.9) (push) Failing after 5m50s
Docker Build and Push / deploy-staging (push) Has been skipped
Service Adapters (Python FastAPI) / build (push) Has been skipped
Service Adapters (Python FastAPI) / security (push) Has been skipped
Docker Build and Push / deploy-production (push) Has been skipped

This commit is contained in:
glenn schrooyen
2025-09-12 17:44:23 +02:00
parent 60da5ea115
commit 8d1755fd52
13 changed files with 642 additions and 336 deletions

View File

@@ -1,20 +1,25 @@
from fastapi import APIRouter, HTTPException, Query, BackgroundTasks
from models.schemas import EventData, EventResponse, EventsResponse, Event
from services.redis_client import redis_client
from datetime import datetime
import json
from datetime import datetime
from fastapi import APIRouter, BackgroundTasks, HTTPException, Query
from models.schemas import Event, EventData, EventResponse, EventsResponse
from services.redis_client import redis_client
router = APIRouter()
@router.post("/publish-event",
response_model=EventResponse,
summary="Publish Event",
description="Publish an event to the Redis message bus",
responses={
200: {"description": "Event published successfully"},
500: {"description": "Failed to publish event"}
},
tags=["Events"])
@router.post(
"/publish-event",
response_model=EventResponse,
summary="Publish Event",
description="Publish an event to the Redis message bus",
responses={
200: {"description": "Event published successfully"},
500: {"description": "Failed to publish event"},
},
tags=["Events"],
)
async def publish_event(event_data: EventData, background_tasks: BackgroundTasks):
"""Publish an event to the Redis message bus for consumption by other services"""
try:
@@ -22,29 +27,33 @@ async def publish_event(event_data: EventData, background_tasks: BackgroundTasks
"timestamp": datetime.now().isoformat(),
"service": event_data.service,
"event_type": event_data.event_type,
"metadata": json.dumps(event_data.metadata)
"metadata": json.dumps(event_data.metadata),
}
# Publish to Redis
redis_client.lpush("events", json.dumps(event))
return EventResponse(
status="published",
event=event
)
return EventResponse(status="published", event=event)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/events",
response_model=EventsResponse,
summary="Get Events",
description="Retrieve recent events from the message bus",
responses={
200: {"description": "Successfully retrieved events"},
500: {"description": "Failed to retrieve events"}
},
tags=["Events"])
async def get_events(limit: int = Query(100, ge=1, le=1000, description="Maximum number of events to retrieve")):
@router.get(
"/events",
response_model=EventsResponse,
summary="Get Events",
description="Retrieve recent events from the message bus",
responses={
200: {"description": "Successfully retrieved events"},
500: {"description": "Failed to retrieve events"},
},
tags=["Events"],
)
async def get_events(
limit: int = Query(
100, ge=1, le=1000, description="Maximum number of events to retrieve"
)
):
"""Get recent events from the Redis message bus"""
try:
events = redis_client.lrange("events", 0, limit - 1)
@@ -55,7 +64,7 @@ async def get_events(limit: int = Query(100, ge=1, le=1000, description="Maximum
parsed_events.append(Event(**event_data))
except json.JSONDecodeError:
continue
return EventsResponse(events=parsed_events)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

View File

@@ -1,27 +1,32 @@
from fastapi import APIRouter, HTTPException
from models.schemas import FrigateEventsResponse, FrigateEvent
from services.config import SERVICES
from datetime import datetime
from fastapi import APIRouter, HTTPException
from models.schemas import FrigateEvent, FrigateEventsResponse
from services.config import SERVICES
router = APIRouter()
@router.get("/frigate/events",
response_model=FrigateEventsResponse,
summary="Get Frigate Events",
description="Retrieve detection events from Frigate NVR",
responses={
200: {"description": "Successfully retrieved events"},
503: {"description": "Frigate integration not configured"}
},
tags=["Frigate"])
@router.get(
"/frigate/events",
response_model=FrigateEventsResponse,
summary="Get Frigate Events",
description="Retrieve detection events from Frigate NVR",
responses={
200: {"description": "Successfully retrieved events"},
503: {"description": "Frigate integration not configured"},
},
tags=["Frigate"],
)
async def get_frigate_events():
"""Get Frigate detection events including person, vehicle, and object detections"""
if not SERVICES["frigate"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Frigate integration not configured. Please set FRIGATE_TOKEN environment variable."
status_code=503,
detail="Frigate integration not configured. Please set FRIGATE_TOKEN environment variable.",
)
# This would make actual API calls to Frigate
# For now, return mock data
return FrigateEventsResponse(
@@ -31,33 +36,36 @@ async def get_frigate_events():
timestamp=datetime.now().isoformat(),
camera="front_door",
label="person",
confidence=0.95
confidence=0.95,
)
]
)
@router.get("/frigate/cameras",
summary="Get Frigate Cameras",
description="Get list of Frigate cameras",
responses={
200: {"description": "Successfully retrieved cameras"},
503: {"description": "Frigate integration not configured"}
},
tags=["Frigate"])
@router.get(
"/frigate/cameras",
summary="Get Frigate Cameras",
description="Get list of Frigate cameras",
responses={
200: {"description": "Successfully retrieved cameras"},
503: {"description": "Frigate integration not configured"},
},
tags=["Frigate"],
)
async def get_frigate_cameras():
"""Get list of available Frigate cameras"""
if not SERVICES["frigate"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Frigate integration not configured. Please set FRIGATE_TOKEN environment variable."
status_code=503,
detail="Frigate integration not configured. Please set FRIGATE_TOKEN environment variable.",
)
# This would make actual API calls to Frigate
# For now, return mock data
return {
"cameras": [
{"name": "front_door", "enabled": True},
{"name": "back_yard", "enabled": True},
{"name": "garage", "enabled": False}
{"name": "garage", "enabled": False},
]
}

View File

@@ -1,39 +1,44 @@
from fastapi import APIRouter
from datetime import datetime
from models.schemas import RootResponse, HealthResponse, ServiceStatus
from fastapi import APIRouter
from models.schemas import HealthResponse, RootResponse, ServiceStatus
from services.config import SERVICES
router = APIRouter()
@router.get("/",
response_model=RootResponse,
summary="API Root",
description="Get basic API information",
tags=["General"])
@router.get(
"/",
response_model=RootResponse,
summary="API Root",
description="Get basic API information",
tags=["General"],
)
async def root():
"""Get basic API information and version"""
return RootResponse(
message="LabFusion Service Adapters API",
version="1.0.0"
)
return RootResponse(message="LabFusion Service Adapters API", version="1.0.0")
@router.get("/health",
response_model=HealthResponse,
summary="Health Check",
description="Check service health status",
tags=["General"])
@router.get(
"/health",
response_model=HealthResponse,
summary="Health Check",
description="Check service health status",
tags=["General"],
)
async def health_check():
"""Check the health status of the service adapters"""
return HealthResponse(
status="healthy",
timestamp=datetime.now().isoformat()
)
return HealthResponse(status="healthy", timestamp=datetime.now().isoformat())
@router.get("/services",
response_model=dict,
summary="Get Service Status",
description="Get status of all configured external services",
tags=["Services"])
@router.get(
"/services",
response_model=dict,
summary="Get Service Status",
description="Get status of all configured external services",
tags=["Services"],
)
async def get_services():
"""Get status of all configured external services (Home Assistant, Frigate, Immich, n8n)"""
service_status = {}
@@ -41,6 +46,6 @@ async def get_services():
service_status[service_name] = ServiceStatus(
enabled=config["enabled"],
url=config["url"],
status="unknown" # Would check actual service status
status="unknown", # Would check actual service status
)
return service_status

View File

@@ -1,26 +1,30 @@
from fastapi import APIRouter, HTTPException, Path
from models.schemas import HAEntitiesResponse, HAEntity, HAAttributes
from models.schemas import HAAttributes, HAEntitiesResponse, HAEntity
from services.config import SERVICES
router = APIRouter()
@router.get("/home-assistant/entities",
response_model=HAEntitiesResponse,
summary="Get Home Assistant Entities",
description="Retrieve all entities from Home Assistant",
responses={
200: {"description": "Successfully retrieved entities"},
503: {"description": "Home Assistant integration not configured"}
},
tags=["Home Assistant"])
@router.get(
"/home-assistant/entities",
response_model=HAEntitiesResponse,
summary="Get Home Assistant Entities",
description="Retrieve all entities from Home Assistant",
responses={
200: {"description": "Successfully retrieved entities"},
503: {"description": "Home Assistant integration not configured"},
},
tags=["Home Assistant"],
)
async def get_ha_entities():
"""Get Home Assistant entities including sensors, switches, and other devices"""
if not SERVICES["home_assistant"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Home Assistant integration not configured. Please set HOME_ASSISTANT_TOKEN environment variable."
status_code=503,
detail="Home Assistant integration not configured. Please set HOME_ASSISTANT_TOKEN environment variable.",
)
# This would make actual API calls to Home Assistant
# For now, return mock data
return HAEntitiesResponse(
@@ -29,46 +33,46 @@ async def get_ha_entities():
entity_id="sensor.cpu_usage",
state="45.2",
attributes=HAAttributes(
unit_of_measurement="%",
friendly_name="CPU Usage"
)
unit_of_measurement="%", friendly_name="CPU Usage"
),
),
HAEntity(
entity_id="sensor.memory_usage",
state="2.1",
attributes=HAAttributes(
unit_of_measurement="GB",
friendly_name="Memory Usage"
)
)
unit_of_measurement="GB", friendly_name="Memory Usage"
),
),
]
)
@router.get("/home-assistant/entity/{entity_id}",
response_model=HAEntity,
summary="Get Specific HA Entity",
description="Get a specific Home Assistant entity by ID",
responses={
200: {"description": "Successfully retrieved entity"},
404: {"description": "Entity not found"},
503: {"description": "Home Assistant integration not configured"}
},
tags=["Home Assistant"])
@router.get(
"/home-assistant/entity/{entity_id}",
response_model=HAEntity,
summary="Get Specific HA Entity",
description="Get a specific Home Assistant entity by ID",
responses={
200: {"description": "Successfully retrieved entity"},
404: {"description": "Entity not found"},
503: {"description": "Home Assistant integration not configured"},
},
tags=["Home Assistant"],
)
async def get_ha_entity(entity_id: str = Path(..., description="Entity ID")):
"""Get a specific Home Assistant entity by its ID"""
if not SERVICES["home_assistant"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Home Assistant integration not configured. Please set HOME_ASSISTANT_TOKEN environment variable."
status_code=503,
detail="Home Assistant integration not configured. Please set HOME_ASSISTANT_TOKEN environment variable.",
)
# This would make actual API calls to Home Assistant
# For now, return mock data
return HAEntity(
entity_id=entity_id,
state="unknown",
attributes=HAAttributes(
unit_of_measurement="",
friendly_name=f"Entity {entity_id}"
)
unit_of_measurement="", friendly_name=f"Entity {entity_id}"
),
)

View File

@@ -1,27 +1,32 @@
from fastapi import APIRouter, HTTPException
from models.schemas import ImmichAssetsResponse, ImmichAsset
from services.config import SERVICES
from datetime import datetime
from fastapi import APIRouter, HTTPException
from models.schemas import ImmichAsset, ImmichAssetsResponse
from services.config import SERVICES
router = APIRouter()
@router.get("/immich/assets",
response_model=ImmichAssetsResponse,
summary="Get Immich Assets",
description="Retrieve photo assets from Immich",
responses={
200: {"description": "Successfully retrieved assets"},
503: {"description": "Immich integration not configured"}
},
tags=["Immich"])
@router.get(
"/immich/assets",
response_model=ImmichAssetsResponse,
summary="Get Immich Assets",
description="Retrieve photo assets from Immich",
responses={
200: {"description": "Successfully retrieved assets"},
503: {"description": "Immich integration not configured"},
},
tags=["Immich"],
)
async def get_immich_assets():
"""Get Immich photo assets including metadata, tags, and face detection results"""
if not SERVICES["immich"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Immich integration not configured. Please set IMMICH_API_KEY environment variable."
status_code=503,
detail="Immich integration not configured. Please set IMMICH_API_KEY environment variable.",
)
# This would make actual API calls to Immich
# For now, return mock data
return ImmichAssetsResponse(
@@ -31,32 +36,35 @@ async def get_immich_assets():
filename="photo_001.jpg",
created_at=datetime.now().isoformat(),
tags=["person", "outdoor"],
faces=["Alice", "Bob"]
faces=["Alice", "Bob"],
)
]
)
@router.get("/immich/albums",
summary="Get Immich Albums",
description="Get list of Immich albums",
responses={
200: {"description": "Successfully retrieved albums"},
503: {"description": "Immich integration not configured"}
},
tags=["Immich"])
@router.get(
"/immich/albums",
summary="Get Immich Albums",
description="Get list of Immich albums",
responses={
200: {"description": "Successfully retrieved albums"},
503: {"description": "Immich integration not configured"},
},
tags=["Immich"],
)
async def get_immich_albums():
"""Get list of Immich albums"""
if not SERVICES["immich"]["enabled"]:
raise HTTPException(
status_code=503,
detail="Immich integration not configured. Please set IMMICH_API_KEY environment variable."
status_code=503,
detail="Immich integration not configured. Please set IMMICH_API_KEY environment variable.",
)
# This would make actual API calls to Immich
# For now, return mock data
return {
"albums": [
{"id": "album_1", "name": "Family Photos", "asset_count": 150},
{"id": "album_2", "name": "Vacation 2024", "asset_count": 75}
{"id": "album_2", "name": "Vacation 2024", "asset_count": 75},
]
}