PMFlow/backend/app/services/signal_source_repair/mixin_repair.py

137 lines
4.5 KiB
Python

"""Repair orchestration for signal source repair service."""
from __future__ import annotations
from .errors import SignalSourceRepairMissingSignalError
from .shared import ProactiveSignalCaptureService
class SignalSourceRepairFlowMixin:
"""Runs single/batch repair orchestration."""
async def _repair_single_registry(
self,
*,
capture_service: ProactiveSignalCaptureService,
tenant_id: int,
project_id: int,
registry_id: int,
sender: str,
timeout_seconds: int,
) -> dict[str, object]:
row = await self._load_registry(
tenant_id=tenant_id,
project_id=project_id,
registry_id=registry_id,
)
if row is None:
return {"registry_id": int(registry_id), "status": "not_found"}
if row.signal_id is not None:
return self._build_item_result(
row=row,
signal_id=self._safe_int(row.signal_id),
capture_status="already_linked",
work_item_id=None,
route_status="",
)
reusable_signal_id = await self._find_reusable_signal_id(
tenant_id=tenant_id,
project_id=project_id,
row=row,
)
if reusable_signal_id is not None:
await self._persist_registry_link(
tenant_id=tenant_id,
project_id=project_id,
row=row,
signal_id=int(reusable_signal_id),
capture_status="relinked",
)
return self._build_item_result(
row=row,
signal_id=int(reusable_signal_id),
capture_status="relinked",
work_item_id=None,
route_status="",
)
capture_result = await self._capture_registry(
capture_service=capture_service,
tenant_id=tenant_id,
project_id=project_id,
sender=sender,
row=row,
stage="base",
timeout_seconds=timeout_seconds,
)
signal_id = self._extract_signal_id(capture_result)
if signal_id is None:
capture_result = await self._capture_registry(
capture_service=capture_service,
tenant_id=tenant_id,
project_id=project_id,
sender=sender,
row=row,
stage="explicit",
timeout_seconds=timeout_seconds,
)
signal_id = self._extract_signal_id(capture_result)
if signal_id is None:
signal_id = await self._find_reusable_signal_id(
tenant_id=tenant_id,
project_id=project_id,
row=row,
)
if signal_id is None:
raise SignalSourceRepairMissingSignalError(
registry_id=int(row.id),
project_id=int(project_id),
status=str(capture_result.get("status") or ""),
)
capture_status = str(capture_result.get("status") or "captured")
await self._persist_registry_link(
tenant_id=tenant_id,
project_id=project_id,
row=row,
signal_id=int(signal_id),
capture_status=capture_status,
)
return self._build_item_result(
row=row,
signal_id=int(signal_id),
capture_status=capture_status,
work_item_id=self._safe_int(capture_result.get("work_item_id")),
route_status=self._extract_route_status(capture_result),
)
async def repair_registries(
self,
*,
tenant_id: int,
project_id: int,
registry_ids: list[int],
sender: str,
) -> list[dict[str, object]]:
capture_service = ProactiveSignalCaptureService(self.db)
timeout_seconds = self._resolve_timeout_seconds()
results: list[dict[str, object]] = []
try:
for registry_id in registry_ids:
item = await self._repair_single_registry(
capture_service=capture_service,
tenant_id=tenant_id,
project_id=project_id,
registry_id=int(registry_id),
sender=sender,
timeout_seconds=timeout_seconds,
)
results.append(item)
await self.db.commit()
except Exception:
await self.db.rollback()
raise
return results