from fastapi import APIRouter, Depends, HTTPException, Query, Request from sqlalchemy import text from sqlalchemy.orm import Session from core_domain import ServiceHealth from core_shared.secrets import SecretCipher from app.application.services import ToolApplicationService from app.bootstrap.settings import ToolServiceSettings from app.db.session import get_db from app.domain.repositories import ( ToolBindingRepository, ToolCredentialRepository, ToolDefinitionRepository, ToolVersionRepository, ) from app.schemas.tool import ( ToolBindingCreateRequest, ToolBindingDetailResponse, ToolBindingResponse, ToolCredentialCreateRequest, ToolCredentialResponse, ToolCredentialRevealResponse, ToolCreateRequest, ToolResponse, ToolVersionCreateRequest, ToolVersionResponse, ) router = APIRouter() def get_tool_application_service( request: Request, db: Session = Depends(get_db), ) -> ToolApplicationService: settings: ToolServiceSettings = request.app.state.settings return ToolApplicationService( tool_definition_repository=ToolDefinitionRepository(db), tool_version_repository=ToolVersionRepository(db), tool_binding_repository=ToolBindingRepository(db), tool_credential_repository=ToolCredentialRepository(db), secret_cipher=SecretCipher(key=settings.credential_encryption_key), ) @router.get("/health", response_model=ServiceHealth) def health_check(db: Session = Depends(get_db)) -> ServiceHealth: db.execute(text("SELECT 1")) return ServiceHealth(service="tool-service", status="ok", database="ok") @router.post("", response_model=ToolResponse) def create_tool_definition( payload: ToolCreateRequest, service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolResponse: entity = service.create_tool_definition(payload) return ToolResponse.from_entity(entity) @router.get("", response_model=list[ToolResponse]) def list_tool_definitions( tenant_id: str = Query(...), service: ToolApplicationService = Depends(get_tool_application_service), ) -> list[ToolResponse]: return [ToolResponse.from_entity(item) for item in service.list_tool_definitions(tenant_id)] @router.post("/versions", response_model=ToolVersionResponse) def create_tool_version( payload: ToolVersionCreateRequest, service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolVersionResponse: entity = service.create_tool_version(payload) return ToolVersionResponse.from_entity(entity) @router.get("/versions", response_model=list[ToolVersionResponse]) def list_tool_versions( tenant_id: str = Query(...), tool_id: str = Query(...), service: ToolApplicationService = Depends(get_tool_application_service), ) -> list[ToolVersionResponse]: return [ ToolVersionResponse.from_entity(item) for item in service.list_tool_versions(tenant_id=tenant_id, tool_id=tool_id) ] @router.post("/bindings", response_model=ToolBindingResponse) def create_tool_binding( payload: ToolBindingCreateRequest, service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolBindingResponse: try: entity = service.create_tool_binding(payload) except ValueError as exc: raise HTTPException(status_code=422, detail=str(exc)) from exc return ToolBindingResponse.from_entity(entity) @router.get("/bindings", response_model=list[ToolBindingResponse]) def list_tool_bindings( tenant_id: str = Query(...), app_id: str | None = Query(default=None), service: ToolApplicationService = Depends(get_tool_application_service), ) -> list[ToolBindingResponse]: return [ ToolBindingResponse.from_entity(item) for item in service.list_tool_bindings(tenant_id=tenant_id, app_id=app_id) ] @router.get("/bindings/{binding_id}", response_model=ToolBindingDetailResponse) def get_tool_binding_detail( binding_id: str, tenant_id: str = Query(...), service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolBindingDetailResponse: result = service.get_tool_binding_detail(tenant_id=tenant_id, binding_id=binding_id) if result is None: raise HTTPException(status_code=404, detail=f"tool_binding not found: {binding_id}") binding, tool_version, tool_definition = result return ToolBindingDetailResponse( binding=ToolBindingResponse.from_entity(binding), tool_version=ToolVersionResponse.from_entity(tool_version), tool_definition=ToolResponse.from_entity(tool_definition), ) @router.post("/credentials", response_model=ToolCredentialResponse) def create_tool_credential( payload: ToolCredentialCreateRequest, service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolCredentialResponse: entity = service.create_tool_credential(payload) return ToolCredentialResponse.from_entity(entity) @router.get("/credentials", response_model=list[ToolCredentialResponse]) def list_tool_credentials( tenant_id: str = Query(...), service: ToolApplicationService = Depends(get_tool_application_service), ) -> list[ToolCredentialResponse]: return [ ToolCredentialResponse.from_entity(item) for item in service.list_tool_credentials(tenant_id) ] @router.post("/credentials/{credential_id}/reveal", response_model=ToolCredentialRevealResponse) def reveal_tool_credential( credential_id: str, tenant_id: str = Query(...), service: ToolApplicationService = Depends(get_tool_application_service), ) -> ToolCredentialRevealResponse: result = service.reveal_tool_credential( tenant_id=tenant_id, credential_id=credential_id, ) if result is None: raise HTTPException(status_code=404, detail=f"tool credential not found: {credential_id}") credential, secret_json = result return ToolCredentialRevealResponse( credential=ToolCredentialResponse.from_entity(credential), secret_json=secret_json, )