routes.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. from fastapi import APIRouter, Depends, HTTPException, Query, Request
  2. from sqlalchemy import text
  3. from sqlalchemy.orm import Session
  4. from core_domain import ServiceHealth
  5. from core_shared.secrets import SecretCipher
  6. from app.application.services import ToolApplicationService
  7. from app.bootstrap.settings import ToolServiceSettings
  8. from app.db.session import get_db
  9. from app.domain.repositories import (
  10. ToolBindingRepository,
  11. ToolCredentialRepository,
  12. ToolDefinitionRepository,
  13. ToolVersionRepository,
  14. )
  15. from app.schemas.tool import (
  16. ToolBindingCreateRequest,
  17. ToolBindingDetailResponse,
  18. ToolBindingResponse,
  19. ToolCredentialCreateRequest,
  20. ToolCredentialResponse,
  21. ToolCredentialRevealResponse,
  22. ToolCreateRequest,
  23. ToolResponse,
  24. ToolVersionCreateRequest,
  25. ToolVersionResponse,
  26. )
  27. router = APIRouter()
  28. def get_tool_application_service(
  29. request: Request,
  30. db: Session = Depends(get_db),
  31. ) -> ToolApplicationService:
  32. settings: ToolServiceSettings = request.app.state.settings
  33. return ToolApplicationService(
  34. tool_definition_repository=ToolDefinitionRepository(db),
  35. tool_version_repository=ToolVersionRepository(db),
  36. tool_binding_repository=ToolBindingRepository(db),
  37. tool_credential_repository=ToolCredentialRepository(db),
  38. secret_cipher=SecretCipher(key=settings.credential_encryption_key),
  39. )
  40. @router.get("/health", response_model=ServiceHealth)
  41. def health_check(db: Session = Depends(get_db)) -> ServiceHealth:
  42. db.execute(text("SELECT 1"))
  43. return ServiceHealth(service="tool-service", status="ok", database="ok")
  44. @router.post("", response_model=ToolResponse)
  45. def create_tool_definition(
  46. payload: ToolCreateRequest,
  47. service: ToolApplicationService = Depends(get_tool_application_service),
  48. ) -> ToolResponse:
  49. entity = service.create_tool_definition(payload)
  50. return ToolResponse.from_entity(entity)
  51. @router.get("", response_model=list[ToolResponse])
  52. def list_tool_definitions(
  53. tenant_id: str = Query(...),
  54. service: ToolApplicationService = Depends(get_tool_application_service),
  55. ) -> list[ToolResponse]:
  56. return [ToolResponse.from_entity(item) for item in service.list_tool_definitions(tenant_id)]
  57. @router.post("/versions", response_model=ToolVersionResponse)
  58. def create_tool_version(
  59. payload: ToolVersionCreateRequest,
  60. service: ToolApplicationService = Depends(get_tool_application_service),
  61. ) -> ToolVersionResponse:
  62. entity = service.create_tool_version(payload)
  63. return ToolVersionResponse.from_entity(entity)
  64. @router.get("/versions", response_model=list[ToolVersionResponse])
  65. def list_tool_versions(
  66. tenant_id: str = Query(...),
  67. tool_id: str = Query(...),
  68. service: ToolApplicationService = Depends(get_tool_application_service),
  69. ) -> list[ToolVersionResponse]:
  70. return [
  71. ToolVersionResponse.from_entity(item)
  72. for item in service.list_tool_versions(tenant_id=tenant_id, tool_id=tool_id)
  73. ]
  74. @router.post("/bindings", response_model=ToolBindingResponse)
  75. def create_tool_binding(
  76. payload: ToolBindingCreateRequest,
  77. service: ToolApplicationService = Depends(get_tool_application_service),
  78. ) -> ToolBindingResponse:
  79. try:
  80. entity = service.create_tool_binding(payload)
  81. except ValueError as exc:
  82. raise HTTPException(status_code=422, detail=str(exc)) from exc
  83. return ToolBindingResponse.from_entity(entity)
  84. @router.get("/bindings", response_model=list[ToolBindingResponse])
  85. def list_tool_bindings(
  86. tenant_id: str = Query(...),
  87. app_id: str | None = Query(default=None),
  88. service: ToolApplicationService = Depends(get_tool_application_service),
  89. ) -> list[ToolBindingResponse]:
  90. return [
  91. ToolBindingResponse.from_entity(item)
  92. for item in service.list_tool_bindings(tenant_id=tenant_id, app_id=app_id)
  93. ]
  94. @router.get("/bindings/{binding_id}", response_model=ToolBindingDetailResponse)
  95. def get_tool_binding_detail(
  96. binding_id: str,
  97. tenant_id: str = Query(...),
  98. service: ToolApplicationService = Depends(get_tool_application_service),
  99. ) -> ToolBindingDetailResponse:
  100. result = service.get_tool_binding_detail(tenant_id=tenant_id, binding_id=binding_id)
  101. if result is None:
  102. raise HTTPException(status_code=404, detail=f"tool_binding not found: {binding_id}")
  103. binding, tool_version, tool_definition = result
  104. return ToolBindingDetailResponse(
  105. binding=ToolBindingResponse.from_entity(binding),
  106. tool_version=ToolVersionResponse.from_entity(tool_version),
  107. tool_definition=ToolResponse.from_entity(tool_definition),
  108. )
  109. @router.post("/credentials", response_model=ToolCredentialResponse)
  110. def create_tool_credential(
  111. payload: ToolCredentialCreateRequest,
  112. service: ToolApplicationService = Depends(get_tool_application_service),
  113. ) -> ToolCredentialResponse:
  114. entity = service.create_tool_credential(payload)
  115. return ToolCredentialResponse.from_entity(entity)
  116. @router.get("/credentials", response_model=list[ToolCredentialResponse])
  117. def list_tool_credentials(
  118. tenant_id: str = Query(...),
  119. service: ToolApplicationService = Depends(get_tool_application_service),
  120. ) -> list[ToolCredentialResponse]:
  121. return [
  122. ToolCredentialResponse.from_entity(item)
  123. for item in service.list_tool_credentials(tenant_id)
  124. ]
  125. @router.post("/credentials/{credential_id}/reveal", response_model=ToolCredentialRevealResponse)
  126. def reveal_tool_credential(
  127. credential_id: str,
  128. tenant_id: str = Query(...),
  129. service: ToolApplicationService = Depends(get_tool_application_service),
  130. ) -> ToolCredentialRevealResponse:
  131. result = service.reveal_tool_credential(
  132. tenant_id=tenant_id,
  133. credential_id=credential_id,
  134. )
  135. if result is None:
  136. raise HTTPException(status_code=404, detail=f"tool credential not found: {credential_id}")
  137. credential, secret_json = result
  138. return ToolCredentialRevealResponse(
  139. credential=ToolCredentialResponse.from_entity(credential),
  140. secret_json=secret_json,
  141. )