refactor(db): split topology.py into a topology/ subpackage
Splits the 694-line topology.py into five submixin files plus a
composing TopologyMixin in topology/__init__.py:
_core.py (~225) topologies CRUD + _assert_pending /
_check_and_bump_version concurrency guards
lans.py (~115) LAN CRUD
deckies.py (~130) topology decky CRUD + list_running_topology_deckies
edges.py (~80) edge CRUD + status-event log
mutations.py (~165) live reconciler queue (atomic claim + state writes)
Sibling submixins call self._assert_pending and
self._check_and_bump_version; MRO resolves them onto TopologyCoreMixin
through the composed SQLModelRepository class.
This commit is contained in:
125
decnet/web/db/sqlmodel_repo/topology/deckies.py
Normal file
125
decnet/web/db/sqlmodel_repo/topology/deckies.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Topology decky CRUD + the running-decky listing the fleet aggregator
|
||||
calls through MRO."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import asc, select, text, update
|
||||
|
||||
from decnet.web.db.models import TopologyDecky
|
||||
from decnet.web.db.sqlmodel_repo._helpers import (
|
||||
_deserialize_json_fields,
|
||||
_serialize_json_fields,
|
||||
)
|
||||
|
||||
|
||||
class TopologyDeckiesMixin:
|
||||
"""``self._assert_pending`` / ``self._check_and_bump_version`` resolve
|
||||
through ``TopologyCoreMixin`` via MRO."""
|
||||
|
||||
async def add_topology_decky(
|
||||
self,
|
||||
data: dict[str, Any],
|
||||
*,
|
||||
expected_version: Optional[int] = None,
|
||||
) -> str:
|
||||
payload = _serialize_json_fields(data, ("services", "decky_config"))
|
||||
async with self._session() as session:
|
||||
await self._check_and_bump_version(
|
||||
session, data["topology_id"], expected_version
|
||||
)
|
||||
row = TopologyDecky(**payload)
|
||||
session.add(row)
|
||||
await session.commit()
|
||||
await session.refresh(row)
|
||||
return row.uuid
|
||||
|
||||
async def update_topology_decky(
|
||||
self,
|
||||
decky_uuid: str,
|
||||
fields: dict[str, Any],
|
||||
*,
|
||||
expected_version: Optional[int] = None,
|
||||
enforce_pending: bool = False,
|
||||
) -> None:
|
||||
if not fields:
|
||||
return
|
||||
payload = _serialize_json_fields(fields, ("services", "decky_config"))
|
||||
payload.setdefault("updated_at", datetime.now(timezone.utc))
|
||||
async with self._session() as session:
|
||||
result = await session.execute(
|
||||
select(TopologyDecky).where(TopologyDecky.uuid == decky_uuid)
|
||||
)
|
||||
d = result.scalar_one_or_none()
|
||||
if d is None:
|
||||
raise ValueError(f"decky {decky_uuid!r} not found")
|
||||
if enforce_pending:
|
||||
await self._assert_pending(session, d.topology_id)
|
||||
if expected_version is not None:
|
||||
await self._check_and_bump_version(
|
||||
session, d.topology_id, expected_version
|
||||
)
|
||||
await session.execute(
|
||||
update(TopologyDecky)
|
||||
.where(TopologyDecky.uuid == decky_uuid)
|
||||
.values(**payload)
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
async def delete_topology_decky(
|
||||
self,
|
||||
decky_uuid: str,
|
||||
*,
|
||||
expected_version: Optional[int] = None,
|
||||
) -> None:
|
||||
"""Cascade-delete a decky + all its edges from a pending topology."""
|
||||
async with self._session() as session:
|
||||
result = await session.execute(
|
||||
select(TopologyDecky).where(TopologyDecky.uuid == decky_uuid)
|
||||
)
|
||||
d = result.scalar_one_or_none()
|
||||
if d is None:
|
||||
return
|
||||
await self._assert_pending(session, d.topology_id)
|
||||
if expected_version is not None:
|
||||
await self._check_and_bump_version(
|
||||
session, d.topology_id, expected_version
|
||||
)
|
||||
await session.execute(
|
||||
text("DELETE FROM topology_edges WHERE decky_uuid = :u"),
|
||||
{"u": decky_uuid},
|
||||
)
|
||||
await session.execute(
|
||||
text("DELETE FROM topology_deckies WHERE uuid = :u"),
|
||||
{"u": decky_uuid},
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
async def list_topology_deckies(
|
||||
self, topology_id: str
|
||||
) -> list[dict[str, Any]]:
|
||||
async with self._session() as session:
|
||||
result = await session.execute(
|
||||
select(TopologyDecky)
|
||||
.where(TopologyDecky.topology_id == topology_id)
|
||||
.order_by(asc(TopologyDecky.name))
|
||||
)
|
||||
return [
|
||||
_deserialize_json_fields(
|
||||
r.model_dump(mode="json"), ("services", "decky_config")
|
||||
)
|
||||
for r in result.scalars().all()
|
||||
]
|
||||
|
||||
async def list_running_topology_deckies(self) -> list[dict[str, Any]]:
|
||||
async with self._session() as session:
|
||||
result = await session.execute(
|
||||
select(TopologyDecky).where(TopologyDecky.state == "running")
|
||||
)
|
||||
return [
|
||||
_deserialize_json_fields(
|
||||
r.model_dump(mode="json"), ("services", "decky_config")
|
||||
)
|
||||
for r in result.scalars().all()
|
||||
]
|
||||
Reference in New Issue
Block a user