Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion tableauserverclient/models/virtual_connection_item.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import copy
import datetime as dt
import json
from typing import Callable
Expand All @@ -10,6 +11,7 @@
from tableauserverclient.models.connection_item import ConnectionItem
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
from tableauserverclient.models.permissions_item import PermissionsRule
from tableauserverclient.models.tag_item import TagItem


class VirtualConnectionItem:
Expand All @@ -26,6 +28,12 @@ def __init__(self, name: str) -> None:
self.owner_id: str | None = None
self.content: dict[str, dict] | None = None
self.certification_note: str | None = None
# Tags on virtual connections are populated by the server on List and
# Get responses since Tableau Server 2026.2 / Cloud April 2026
# (REST API 3.30, W-21424436). Callers on earlier server versions
# will see these as empty sets; add_tags / delete_tags still work.
self.tags: set[str] = set()
self._initial_tags: set[str] = set()

def __str__(self) -> str:
return f"{self.__class__.__qualname__}(name={self.name})"
Expand All @@ -43,7 +51,7 @@ def id(self) -> str | None:
@property
def permissions(self) -> list[PermissionsRule]:
if self._permissions is None:
error = "Workbook item must be populated with permissions first."
error = "Virtual connection item must be populated with permissions first."
raise UnpopulatedPropertyError(error)
return self._permissions()

Expand Down Expand Up @@ -71,6 +79,10 @@ def from_xml(cls, xml: Element, ns: dict[str, str]) -> "VirtualConnectionItem":
v_conn.project_id = p.get("id", None) if ((p := xml.find(".//t:project[@id]", ns)) is not None) else None
v_conn.owner_id = o.get("id", None) if ((o := xml.find(".//t:owner[@id]", ns)) is not None) else None
v_conn.content = json.loads(c.text or "{}") if ((c := xml.find(".//t:content", ns)) is not None) else None
tags_elem = xml.find(".//t:tags", ns)
if tags_elem is not None:
v_conn.tags = TagItem.from_xml_element(tags_elem, ns)
v_conn._initial_tags = copy.copy(v_conn.tags)
return v_conn


Expand Down
Loading
Loading