feat(topology): pending-only mutation repo methods with cascade + guards
MazeNET phase 2 step 6. Equips the repo layer with the CRUD the web editor needs before deploy. - TopologyNotEditable exception: raised when a pending-only method hits a non-pending topology. The intent is "free-form edits stop at deploy; the mutator (step 7) takes over for live topologies." - _assert_pending helper checks status inside the session. - update_lan / update_topology_decky accept enforce_pending=True for pre-deploy callers (existing internal callers default to False so behavior is unchanged). - delete_lan: cascades edges; refuses if any decky has only one edge (= this LAN is its home) to prevent orphans. - delete_topology_decky: cascades edges. - delete_topology_edge: bare-bones removal. All four mutators accept expected_version for optimistic concurrency. Existing tests continue to pass (no behavior change for persist/deploy).
This commit is contained in:
@@ -53,6 +53,24 @@ class TopologyStatusError(ValueError):
|
||||
"""Raised when an illegal topology status transition is attempted."""
|
||||
|
||||
|
||||
class TopologyNotEditable(RuntimeError):
|
||||
"""Raised when a pending-only mutation hits a non-pending topology.
|
||||
|
||||
Pre-deploy edits (update_lan, delete_lan, update/delete decky,
|
||||
delete_edge) are only legal while the topology is ``pending``.
|
||||
After deploy the mutator's reconciler + topology_mutations table
|
||||
take over.
|
||||
"""
|
||||
|
||||
def __init__(self, *, status: str, reason: str = "") -> None:
|
||||
self.status = status
|
||||
self.reason = reason
|
||||
super().__init__(
|
||||
f"topology not editable (status={status!r})"
|
||||
+ (f": {reason}" if reason else "")
|
||||
)
|
||||
|
||||
|
||||
class VersionConflict(RuntimeError):
|
||||
"""Raised when a topology write is supplied a stale ``expected_version``.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user