From aa6845b32b2e765797218844c70b3dec88e704f2 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 23 May 2026 14:57:58 +0800 Subject: [PATCH 1/5] feat: add show_filters_icon property to Link for controlling Web UI filter icon display This commit adds a new `show_filters_icon` property to the Link class, allowing users to control whether filter icons are displayed in the Web UI at the individual link level. **Changes:** - Added `_show_filters_icon` attribute to Link class (default: True) - Added `show_filters_icon` property getter - Added `update_show_filters_icon()` method for updating the property - Updated `asdict()` to include the new field in both topology and regular dumps - Added `show_filters_icon` field to LinkBase schema - Updated API routes to handle the new field in create and update operations **API Impact:** - POST /v3/projects/{project_id}/links - accepts `show_filters_icon` in request body - PUT /v3/projects/{project_id}/links/{link_id} - can update `show_filters_icon` - GET /v3/projects/{project_id}/links/{link_id} - returns `show_filters_icon` field **Future Applications:** This feature provides granular control for future AI fault injection modules to manage link-level protocol failures while maintaining clean UI presentation. --- gns3server/api/routes/controller/links.py | 4 ++++ gns3server/controller/link.py | 21 +++++++++++++++++++++ gns3server/schemas/controller/links.py | 1 + 3 files changed, 26 insertions(+) diff --git a/gns3server/api/routes/controller/links.py b/gns3server/api/routes/controller/links.py index 6afa6f7cc..ce9caf450 100644 --- a/gns3server/api/routes/controller/links.py +++ b/gns3server/api/routes/controller/links.py @@ -107,6 +107,8 @@ async def create_link(project_id: UUID, link_data: schemas.LinkCreate) -> schema await link.update_link_style(link_data["link_style"]) if "suspend" in link_data: await link.update_suspend(link_data["suspend"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) try: for node in link_data["nodes"]: await link.add_node( @@ -171,6 +173,8 @@ async def update_link(link_data: schemas.LinkUpdate, link: Link = Depends(dep_li await link.update_link_style(link_data["link_style"]) if "suspend" in link_data: await link.update_suspend(link_data["suspend"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) if "nodes" in link_data: await link.update_nodes(link_data["nodes"]) return link.asdict() diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index ecd74b045..8ba8bc410 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -89,6 +89,7 @@ class Link: self._filters = {} self._link_style = {} self._wireshark = False + self._show_filters_icon = True @property def filters(self): @@ -97,6 +98,13 @@ class Link: """ return self._filters + @property + def show_filters_icon(self): + """ + Get whether to show filters icon in Web UI + """ + return self._show_filters_icon + @property def project(self): """ @@ -167,6 +175,17 @@ class Link: self._project.emit_notification("link.updated", self.asdict()) self._project.dump() + async def update_show_filters_icon(self, value): + """ + Update the show_filters_icon property. + + :param value: Boolean indicating whether to show filters icon in Web UI + """ + if value != self._show_filters_icon: + self._show_filters_icon = value + self._project.emit_notification("link.updated", self.asdict()) + self._project.dump() + async def update_link_style(self, link_style): if link_style != self._link_style: self._link_style = link_style @@ -555,6 +574,7 @@ class Link: "filters": self._filters, "link_style": self._link_style, "suspend": self._suspended, + "show_filters_icon": self._show_filters_icon, } return { "nodes": res, @@ -569,4 +589,5 @@ class Link: "suspend": self._suspended, "link_style": self._link_style, "wireshark": self._wireshark, + "show_filters_icon": self._show_filters_icon, } diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 258696ea3..211d60f23 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -62,6 +62,7 @@ class LinkBase(BaseModel): suspend: Optional[bool] = None link_style: Optional[LinkStyle] = None filters: Optional[dict] = None + show_filters_icon: Optional[bool] = None class LinkCreate(LinkBase): From 367c565d7cf36ab3614c748015926f56a501c96e Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 23 May 2026 15:21:44 +0800 Subject: [PATCH 2/5] fix: ensure show_filters_icon is always returned in API responses Set default value for show_filters_icon in LinkBase schema to ensure the field is always included in API responses, even when response_model_exclude_unset=True is used. --- gns3server/schemas/controller/links.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 211d60f23..6865e2462 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -62,7 +62,7 @@ class LinkBase(BaseModel): suspend: Optional[bool] = None link_style: Optional[LinkStyle] = None filters: Optional[dict] = None - show_filters_icon: Optional[bool] = None + show_filters_icon: Optional[bool] = Field(default=True, description="Show filters icon in Web UI") class LinkCreate(LinkBase): From a1f49427462a1675c901252461f89dfa3d6d1ce8 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 23 May 2026 16:57:39 +0800 Subject: [PATCH 3/5] feat: add show_filters_icon property to Link for controlling Web UI filter icon display This commit adds a new property to the Link class, allowing users to control whether filter icons are displayed in the Web UI at the individual link level. **Changes:** - Added attribute to Link class (default: True) - Added property getter - Added method for updating the property - Updated to include the new field with backward compatibility - Added field to LinkBase schema using Optional[bool] = Field(True, ...) pattern - Updated API routes to handle the new field in create and update operations - Added loading logic for show_filters_icon in project.open() to preserve settings when reopening projects **Schema Definition:** Uses the same pattern as the field: **API Impact:** - POST /v3/projects/{project_id}/links - accepts in request body - PUT /v3/projects/{project_id}/links/{link_id} - can update - GET /v3/projects/{project_id}/links/{link_id} - returns field **Future Applications:** This feature provides granular control for future AI fault injection modules to manage link-level protocol failures while maintaining clean UI presentation. --- gns3server/controller/link.py | 4 ++-- gns3server/controller/project.py | 2 ++ gns3server/schemas/controller/links.py | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index 8ba8bc410..fe8d67e25 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -574,7 +574,7 @@ class Link: "filters": self._filters, "link_style": self._link_style, "suspend": self._suspended, - "show_filters_icon": self._show_filters_icon, + "show_filters_icon": getattr(self, '_show_filters_icon', True), } return { "nodes": res, @@ -589,5 +589,5 @@ class Link: "suspend": self._suspended, "link_style": self._link_style, "wireshark": self._wireshark, - "show_filters_icon": self._show_filters_icon, + "show_filters_icon": getattr(self, '_show_filters_icon', True), } diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index e0a38838c..d9c30a703 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -1208,6 +1208,8 @@ class Project: await link.update_filters(link_data["filters"]) if "link_style" in link_data: await link.update_link_style(link_data["link_style"]) + if "show_filters_icon" in link_data: + await link.update_show_filters_icon(link_data["show_filters_icon"]) for node_link in link_data.get("nodes", []): node = self.get_node(node_link["node_id"]) port = node.get_port(node_link["adapter_number"], node_link["port_number"]) diff --git a/gns3server/schemas/controller/links.py b/gns3server/schemas/controller/links.py index 6865e2462..be6846bb8 100644 --- a/gns3server/schemas/controller/links.py +++ b/gns3server/schemas/controller/links.py @@ -62,7 +62,10 @@ class LinkBase(BaseModel): suspend: Optional[bool] = None link_style: Optional[LinkStyle] = None filters: Optional[dict] = None - show_filters_icon: Optional[bool] = Field(default=True, description="Show filters icon in Web UI") + show_filters_icon: Optional[bool] = Field( + True, + description="Show filters icon in Web UI" + ) class LinkCreate(LinkBase): From 31d8046e303a302ac220526b60efa3e4e92358b4 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 23 May 2026 17:09:50 +0800 Subject: [PATCH 4/5] fix: add getattr fallback to show_filters_icon property for backward compatibility --- gns3server/controller/link.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/controller/link.py b/gns3server/controller/link.py index fe8d67e25..14ff447ea 100644 --- a/gns3server/controller/link.py +++ b/gns3server/controller/link.py @@ -103,7 +103,7 @@ class Link: """ Get whether to show filters icon in Web UI """ - return self._show_filters_icon + return getattr(self, '_show_filters_icon', True) @property def project(self): From fca4459e5fbbd0ee9523c8be0adff603e4e83c8d Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sun, 24 May 2026 20:48:58 +0800 Subject: [PATCH 5/5] fix: update test_json expected output to include show_filters_icon field --- tests/controller/test_link.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/controller/test_link.py b/tests/controller/test_link.py index 86784ef7e..239682948 100644 --- a/tests/controller/test_link.py +++ b/tests/controller/test_link.py @@ -221,6 +221,7 @@ async def test_json(project, compute): } ], "filters": {}, + "show_filters_icon": True, "link_style": {}, "suspend": False, 'wireshark': False, @@ -254,6 +255,7 @@ async def test_json(project, compute): ], "link_style": {}, "filters": {}, + "show_filters_icon": True, "suspend": False }