You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

76 lines
3.2 KiB

from __future__ import annotations
from fastapi import APIRouter, Depends
from app.api.dependencies import get_export_service
from app.core.exceptions import ValidationError
from app.schemas.export import (
MetadataExportResponse,
MetadataImportRequest,
MetadataImportResponse,
MetadataRestorePlanResponse,
MetadataValidationResponse,
)
from app.services.export_service import ExportService
from app.services.job_service import JobService
router = APIRouter()
@router.get("/metadata", response_model=MetadataExportResponse)
async def export_metadata(service: ExportService = Depends(get_export_service)) -> MetadataExportResponse:
data = await service.export_metadata()
exported_at = data.pop("exported_at")
return MetadataExportResponse(exported_at=exported_at, data=data)
@router.get("/metadata/integrity", response_model=MetadataValidationResponse)
async def metadata_integrity(service: ExportService = Depends(get_export_service)) -> MetadataValidationResponse:
result = await service.verify_runtime_integrity()
return MetadataValidationResponse(ok=result["ok"], issues=result["issues"])
@router.post("/metadata/validate", response_model=MetadataValidationResponse)
async def validate_metadata(
payload: MetadataImportRequest,
service: ExportService = Depends(get_export_service),
) -> MetadataValidationResponse:
result = await service.validate_metadata_snapshot(payload.data)
return MetadataValidationResponse(ok=result["ok"], issues=result["issues"])
@router.post("/metadata/restore-plan", response_model=MetadataRestorePlanResponse)
async def restore_plan(
payload: MetadataImportRequest,
service: ExportService = Depends(get_export_service),
) -> MetadataRestorePlanResponse:
result = await service.build_restore_plan(payload.data)
return MetadataRestorePlanResponse(
ok=result["ok"],
issues=result["issues"],
validation_token=result["validation_token"],
summary=result["summary"],
)
@router.post("/metadata/import", response_model=MetadataImportResponse)
async def import_metadata(
payload: MetadataImportRequest,
service: ExportService = Depends(get_export_service),
) -> MetadataImportResponse:
if not payload.confirm_replace:
raise ValidationError("Metadata import requires confirm_replace=true.")
restore_plan_result = await service.build_restore_plan(payload.data)
if not restore_plan_result["ok"]:
raise ValidationError("Metadata snapshot is invalid and cannot be imported.")
if payload.validation_token != restore_plan_result["validation_token"]:
raise ValidationError("Metadata import requires a current validation_token from restore-plan.")
imported_tables = await service.import_metadata(payload.data)
job_service = JobService(service.session)
reconcile_job = await job_service.enqueue_full_reconcile()
health_job_count = 0
for backend in await job_service.backend_repository.list_enabled():
await job_service.enqueue_backend_health_check(backend.id)
health_job_count += 1
scheduled_jobs = [reconcile_job.kind] + ["check_backend_health"] * health_job_count
return MetadataImportResponse(imported_tables=imported_tables, scheduled_jobs=scheduled_jobs)