generated from coulomb/repo-seed
Add admin sync hot reload path
This commit is contained in:
97
src/activity_core/sync_service.py
Normal file
97
src/activity_core/sync_service.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user