173 lines
4.8 KiB
Python
173 lines
4.8 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from dataclasses import asdict
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from fastapi import Depends, FastAPI, HTTPException
|
||
|
|
from pydantic import BaseModel, Field
|
||
|
|
|
||
|
|
from repo_registry.core.service import RegistryService
|
||
|
|
from repo_registry.storage.sqlite import NotFoundError, RegistryStore
|
||
|
|
|
||
|
|
|
||
|
|
class Settings(BaseModel):
|
||
|
|
database_path: str = Field(default="var/repo-registry.sqlite3")
|
||
|
|
|
||
|
|
|
||
|
|
def get_settings() -> Settings:
|
||
|
|
return Settings()
|
||
|
|
|
||
|
|
|
||
|
|
def get_service(settings: Settings = Depends(get_settings)) -> RegistryService:
|
||
|
|
database_path = Path(settings.database_path)
|
||
|
|
database_path.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
store = RegistryStore(database_path)
|
||
|
|
store.initialize()
|
||
|
|
return RegistryService(store)
|
||
|
|
|
||
|
|
|
||
|
|
class RepositoryCreate(BaseModel):
|
||
|
|
name: str
|
||
|
|
url: str
|
||
|
|
description: str | None = None
|
||
|
|
branch: str = "main"
|
||
|
|
|
||
|
|
|
||
|
|
class AbilityCreate(BaseModel):
|
||
|
|
name: str
|
||
|
|
description: str = ""
|
||
|
|
confidence: float = Field(default=1.0, ge=0.0, le=1.0)
|
||
|
|
|
||
|
|
|
||
|
|
class CapabilityCreate(BaseModel):
|
||
|
|
ability_id: int
|
||
|
|
name: str
|
||
|
|
description: str = ""
|
||
|
|
inputs: list[str] = Field(default_factory=list)
|
||
|
|
outputs: list[str] = Field(default_factory=list)
|
||
|
|
confidence: float = Field(default=1.0, ge=0.0, le=1.0)
|
||
|
|
|
||
|
|
|
||
|
|
class FeatureCreate(BaseModel):
|
||
|
|
capability_id: int
|
||
|
|
name: str
|
||
|
|
type: str
|
||
|
|
location: str = ""
|
||
|
|
confidence: float = Field(default=1.0, ge=0.0, le=1.0)
|
||
|
|
|
||
|
|
|
||
|
|
class EvidenceCreate(BaseModel):
|
||
|
|
capability_id: int
|
||
|
|
type: str
|
||
|
|
reference: str
|
||
|
|
strength: str = "medium"
|
||
|
|
|
||
|
|
|
||
|
|
app = FastAPI(title="Repository Ability Registry", version="0.1.0")
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/health")
|
||
|
|
def health() -> dict[str, str]:
|
||
|
|
return {"status": "ok"}
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/repos", status_code=201)
|
||
|
|
def create_repository(
|
||
|
|
payload: RepositoryCreate,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, object]:
|
||
|
|
try:
|
||
|
|
repository = service.register_repository(**payload.model_dump())
|
||
|
|
except ValueError as exc:
|
||
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||
|
|
return asdict(repository)
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/repos")
|
||
|
|
def list_repositories(
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> list[dict[str, object]]:
|
||
|
|
return [asdict(repository) for repository in service.list_repositories()]
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/repos/{repository_id}")
|
||
|
|
def get_repository(
|
||
|
|
repository_id: int,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, object]:
|
||
|
|
try:
|
||
|
|
return asdict(service.get_repository(repository_id))
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/repos/{repository_id}/abilities", status_code=201)
|
||
|
|
def create_ability(
|
||
|
|
repository_id: int,
|
||
|
|
payload: AbilityCreate,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, int]:
|
||
|
|
try:
|
||
|
|
ability_id = service.add_ability(repository_id, **payload.model_dump())
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
return {"id": ability_id}
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/repos/{repository_id}/capabilities", status_code=201)
|
||
|
|
def create_capability(
|
||
|
|
repository_id: int,
|
||
|
|
payload: CapabilityCreate,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, int]:
|
||
|
|
try:
|
||
|
|
capability_id = service.add_capability(repository_id, **payload.model_dump())
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
return {"id": capability_id}
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/repos/{repository_id}/features", status_code=201)
|
||
|
|
def create_feature(
|
||
|
|
repository_id: int,
|
||
|
|
payload: FeatureCreate,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, int]:
|
||
|
|
try:
|
||
|
|
feature_id = service.add_feature(repository_id, **payload.model_dump())
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
return {"id": feature_id}
|
||
|
|
|
||
|
|
|
||
|
|
@app.post("/repos/{repository_id}/evidence", status_code=201)
|
||
|
|
def create_evidence(
|
||
|
|
repository_id: int,
|
||
|
|
payload: EvidenceCreate,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, int]:
|
||
|
|
try:
|
||
|
|
evidence_id = service.add_evidence(repository_id, **payload.model_dump())
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
return {"id": evidence_id}
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/repos/{repository_id}/ability-map")
|
||
|
|
def get_ability_map(
|
||
|
|
repository_id: int,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> dict[str, object]:
|
||
|
|
try:
|
||
|
|
return asdict(service.ability_map(repository_id))
|
||
|
|
except NotFoundError as exc:
|
||
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||
|
|
|
||
|
|
|
||
|
|
@app.get("/search")
|
||
|
|
def search(
|
||
|
|
q: str,
|
||
|
|
service: RegistryService = Depends(get_service),
|
||
|
|
) -> list[dict[str, object]]:
|
||
|
|
return [asdict(result) for result in service.search(q)]
|