mirror of
https://github.com/hansjone/netx.git
synced 2026-10-16 21:10:50 +08:00
Optimize topology canvas per the 2026-10-12 plan.
Save path: one atomic POST /views/{id}/commit (edges, ports, styles, node
add/remove, positions) replaces the multi-request save. Edge styles and
manual-edge port edits now go through dirty/undo/save instead of writing
immediately; non-manual edge ports are read-only.
Data safety: UI saves no longer clear MCP node locks or freeze the device
name into the placement label (label_override + omit-means-keep semantics,
also applied to the MCP positions tool). Adding NEs/neighbors with unsaved
edits now saves first instead of silently dropping them. Positions no longer
bypass membership caps, except persisting synthesized child-region icons.
Fixes found in acceptance: nodes added in the same commit kept (40,40)
under autoflush=False; edits on edges deleted in the same save 404'd the
whole save; box-selection drags and arrow-key moves were not marked dirty;
large canvases far from the origin opened blank and locate did nothing
because fitView waits for measurement that onlyRenderVisibleElements never
does (now fitBounds from known geometry).
Also: batch N+1 lookups in graph/positions, deterministic truncation,
outside-peer scan excludes internal edges, split TopologyPage into hooks,
fix flaky UME/LLDP port test, add scripts/topology_clear_frozen_labels.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
60a0ea6021
commit
f301544b99
40 changed files with 5587 additions and 1916 deletions
|
|
@ -273,8 +273,10 @@ class ViewNodeIn(BaseModel):
|
|||
fabric_node_id: str = Field(min_length=1, max_length=64)
|
||||
x: float = 0.0
|
||||
y: float = 0.0
|
||||
label: str = ""
|
||||
locked: bool = False
|
||||
# None = leave existing override unchanged; "" = clear override (follow device name).
|
||||
label: str | None = None
|
||||
# None = leave lock unchanged; False unlocks (and allows move); True locks after move.
|
||||
locked: bool | None = None
|
||||
|
||||
|
||||
class ViewNodeOut(BaseModel):
|
||||
|
|
@ -282,6 +284,8 @@ class ViewNodeOut(BaseModel):
|
|||
managed_ne_id: str = ""
|
||||
ume_ne_id: str = ""
|
||||
label: str = ""
|
||||
# Raw placement override (empty = follow fabric name/ip). ``label`` is the display value.
|
||||
label_override: str = ""
|
||||
x: float = 0.0
|
||||
y: float = 0.0
|
||||
locked: bool = False
|
||||
|
|
@ -448,6 +452,16 @@ class ViewNodesRemove(BaseModel):
|
|||
|
||||
|
||||
class ViewEdgeStylePatch(BaseModel):
|
||||
"""Single-edge style patch (legacy). Prefer ``items`` for batch."""
|
||||
|
||||
fabric_edge_id: str = ""
|
||||
stroke_color: str = ""
|
||||
stroke_width: int = Field(default=0, ge=0, le=12)
|
||||
line_style: str = Field(default="", max_length=16)
|
||||
items: list["ViewEdgeStyleItem"] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ViewEdgeStyleItem(BaseModel):
|
||||
fabric_edge_id: str
|
||||
stroke_color: str = ""
|
||||
stroke_width: int = Field(default=0, ge=0, le=12)
|
||||
|
|
@ -461,6 +475,11 @@ class FabricManualEdgeIn(BaseModel):
|
|||
b_port: str = ""
|
||||
|
||||
|
||||
class FabricEdgePortsPatch(BaseModel):
|
||||
a_port: str = ""
|
||||
b_port: str = ""
|
||||
|
||||
|
||||
class FabricEdgesDeleteRequest(BaseModel):
|
||||
edge_ids: list[str] = Field(default_factory=list, min_length=1)
|
||||
|
||||
|
|
@ -469,6 +488,40 @@ class FabricEdgesDeleteOut(BaseModel):
|
|||
deleted: int = 0
|
||||
|
||||
|
||||
class ViewCommitEdgeCreate(BaseModel):
|
||||
client_id: str = ""
|
||||
a_node_id: str = Field(min_length=1, max_length=64)
|
||||
b_node_id: str = Field(min_length=1, max_length=64)
|
||||
a_port: str = ""
|
||||
b_port: str = ""
|
||||
|
||||
|
||||
class ViewCommitEdgePortEdit(BaseModel):
|
||||
id: str = Field(min_length=1, max_length=64)
|
||||
a_port: str = ""
|
||||
b_port: str = ""
|
||||
|
||||
|
||||
class ViewCommitIn(BaseModel):
|
||||
"""Atomic canvas save: all steps succeed or the whole request rolls back."""
|
||||
|
||||
edge_creates: list[ViewCommitEdgeCreate] = Field(default_factory=list)
|
||||
edge_deletes: list[str] = Field(default_factory=list)
|
||||
edge_port_edits: list[ViewCommitEdgePortEdit] = Field(default_factory=list)
|
||||
edge_styles: list[ViewEdgeStyleItem] = Field(default_factory=list)
|
||||
node_adds: list[str] = Field(default_factory=list)
|
||||
node_removes: list[str] = Field(default_factory=list)
|
||||
positions: list[ViewNodeIn] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ViewCommitOut(BaseModel):
|
||||
ok: bool = True
|
||||
view_id: str = ""
|
||||
edge_id_map: dict[str, str] = Field(default_factory=dict)
|
||||
skipped_locked: int = 0
|
||||
graph: TopologyViewGraphOut | None = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Classify rules + slices + search
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue