Files
activity-core/src/activity_core/sync_service.py

98 lines
2.9 KiB
Python

"""Shared ActivityDefinition/event type/schedule sync orchestration."""
from __future__ import annotations
from typing import Any
from temporalio.client import Client
from activity_core.event_type_registry import sync_event_types
from activity_core.sync_activity_definitions import sync as sync_activity_definitions
from activity_core.sync_schedules import ScheduleSyncResult, sync_with_session_factory
_MAX_ERRORS = 20
_MAX_ERROR_MESSAGE_LENGTH = 1000
def _empty_result(
*,
definitions: bool,
schedules: bool,
event_types: bool,
) -> dict[str, Any]:
return {
"ok": True,
"ran": {
"definitions": definitions,
"schedules": schedules,
"event_types": event_types,
},
"definitions": {"synced": 0},
"event_types": {"synced": 0},
"schedules": ScheduleSyncResult().to_dict(),
"errors": [],
}
def _record_error(result: dict[str, Any], stage: str, exc: Exception) -> None:
errors = result["errors"]
if len(errors) >= _MAX_ERRORS:
return
errors.append(
{
"stage": stage,
"type": type(exc).__name__,
"message": str(exc)[:_MAX_ERROR_MESSAGE_LENGTH],
}
)
result["ok"] = False
async def run_sync(
*,
session_factory: Any,
temporal_client: Client | None,
definitions: bool = True,
schedules: bool = True,
event_types: bool = False,
) -> dict[str, Any]:
"""Run the requested sync stages and return bounded operator-facing status.
The orchestration deliberately accepts its database and Temporal
dependencies as arguments so startup and the API can share the same behavior
without creating another global runtime.
"""
result = _empty_result(
definitions=definitions,
schedules=schedules,
event_types=event_types,
)
if definitions:
try:
result["definitions"]["synced"] = await sync_activity_definitions(
session_factory
)
except Exception as exc: # pragma: no cover - exercised through tests
_record_error(result, "definitions", exc)
if event_types:
try:
result["event_types"]["synced"] = await sync_event_types(session_factory)
except Exception as exc: # pragma: no cover - exercised through tests
_record_error(result, "event_types", exc)
if schedules:
try:
if temporal_client is None:
raise RuntimeError("Temporal client is required for schedule sync")
schedule_result = await sync_with_session_factory(
temporal_client,
session_factory,
)
result["schedules"] = schedule_result.to_dict()
except Exception as exc: # pragma: no cover - exercised through tests
_record_error(result, "schedules", exc)
return result