8000 Merge pull request #1405 from jorwoods/jorwoods/groupsets · tableau/server-client-python@3a53d00 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3a53d00

Browse files
authored
Merge pull request #1405 from jorwoods/jorwoods/groupsets
feat: add support for groupsets
2 parents e980142 + 6351d36 commit 3a53d00

File tree

12 files changed

+327
-0
lines changed

12 files changed

+327
-0
lines changed

tableauserverclient/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
FlowRunItem,
1818
FileuploadItem,
1919
GroupItem,
20+
GroupSetItem,
2021
HourlyInterval,
2122
IntervalItem,
2223
JobItem,
@@ -79,6 +80,7 @@
7980
"FlowRunItem",
8081
"FileuploadItem",
8182
"GroupItem",
83+
"GroupSetItem",
8284
"HourlyInterval",
8385
"IntervalItem",
8486
"JobItem",

tableauserverclient/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from tableauserverclient.models.flow_item import FlowItem
1515
from tableauserverclient.models.flow_run_item import FlowRunItem
1616
from tableauserverclient.models.group_item import GroupItem
17+
from tableauserverclient.models.groupset_item import GroupSetItem
1718
from tableauserverclient.models.interval_item import (
1819
IntervalItem,
1920
DailyInterval,
@@ -60,6 +61,7 @@
6061
"FlowItem",
6162
"FlowRunItem",
6263
"GroupItem",
64+
"GroupSetItem",
6365
"IntervalItem",
6466
"JobItem",
6567
"DailyInterval",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from typing import Dict, List, Optional
2+
import xml.etree.ElementTree as ET
3+
4+
from defusedxml.ElementTree import fromstring
5+
6+
from tableauserverclient.models.group_item import GroupItem
7+
8+
9+
class GroupSetItem:
10+
tag_name: str = "groupSet"
11+
12+
def __init__(self, name: Optional[str] = None) -> None:
13+
self.name = name
14+
self.id: Optional[str] = None
15+
self.groups: List["GroupItem"] = []
16+
self.group_count: int = 0
17+
18+
def __str__(self) -> str:
19+
name = self.name
20+
id = self.id
21+
return f"<{self.__class__.__qualname__}({name=}, {id=})>"
22+
23+
def __repr__(self) -> str:
24+
return self.__str__()
25+
26+
@classmethod
27+
def from_response(cls, response: bytes, ns: Dict[str, str]) -> List["GroupSetItem"]:
28+
parsed_response = fromstring(response)
29+
all_groupset_xml = parsed_response.findall(".//t:groupSet", namespaces=ns)
30+
return [cls.from_xml(xml, ns) for xml in all_groupset_xml]
31+
32+
@classmethod
33+
def from_xml(cls, groupset_xml: ET.Element, ns: Dict[str, str]) -> "GroupSetItem":
34+
def get_group(group_xml: ET.Element) -> GroupItem:
35+
group_item = GroupItem()
36+
group_item._id = group_xml.get("id")
37+
group_item.name = group_xml.get("name")
38+
return group_item
39+
40+
group_set_item = cls()
41+
group_set_item.name = groupset_xml.get("name")
42+
group_set_item.id = groupset_xml.get("id")
43+
group_set_item.group_count = int(count) if (count := groupset_xml.get("groupCount")) else 0
44+
group_set_item.groups = [
45+
get_group(group_xml) for group_xml in groupset_xml.findall(".//t:group", namespaces=ns)
46+
]
47+
48+
return group_set_item

tableauserverclient/server/endpoint/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from tableauserverclient.server.endpoint.flows_endpoint import Flows
1313
from tableauserverclient.server.endpoint.flow_task_endpoint import FlowTasks
1414
from tableauserverclient.server.endpoint.groups_endpoint import Groups
15+
from tableauserverclient.server.endpoint.groupsets_endpoint import GroupSets
1516
from tableauserverclient.server.endpoint.jobs_endpoint import Jobs
1617
from tableauserverclient.server.endpoint.metadata_endpoint import Metadata
1718
from tableauserverclient.server.endpoint.metrics_endpoint import Metrics
@@ -43,6 +44,7 @@
4344
"Flows",
4445
"FlowTasks",
4546
"Groups",
47+
"GroupSets",
4648
"Jobs",
4749
"Metadata",
4850
"Metrics",
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from typing import List, Literal, Optional, Tuple, TYPE_CHECKING, Union
2+
3+
from tableauserverclient.helpers.logging import logger
4+
from tableauserverclient.models.group_item import GroupItem
5+
from tableauserverclient.models.groupset_item import GroupSetItem
6+
from tableauserverclient.models.pagination_item import PaginationItem
7+
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint
8+
from tableauserverclient.server.request_options import RequestOptions
9+
from tableauserverclient.server.request_factory import RequestFactory
10+
from tableauserverclient.server.endpoint.endpoint import api
11+
12+
if TYPE_CHECKING:
13+
from tableauserverclient.server import Server
14+
15+
16+
class GroupSets(QuerysetEndpoint[GroupSetItem]):
17+
def __init__(self, parent_srv: "Server") -> None:
18+
super().__init__(parent_srv)
19+
20+
@property
21+
def baseurl(self) -> str:
22+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/groupsets"
23+
24+
@api(version="3.22")
25+
def get(
26+
self,
27+
request_options: Optional[RequestOptions] = None,
28+
result_level: Optional[Literal["members", "local"]] = None,
29+
) -> Tuple[List[GroupSetItem], PaginationItem]:
30+
logger.info("Querying all group sets on site")
31+
url = self.baseurl
32+
if result_level:
33+
url += f"?resultlevel={result_level}"
34+
server_response = self.get_request(url, request_options)
35+
pagination_item = PaginationItem.from_response(server_response.content, self.parent_srv.namespace)
36+
all_group_set_items = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
37+
return all_group_set_items, pagination_item
38+
39+
@api(version="3.22")
40+
def get_by_id(self, groupset_id: str) -> GroupSetItem:
41+
logger.info(f"Querying group set (ID: {groupset_id})")
42+
url = f"{self.baseurl}/{groupset_id}"
43+
server_response = self.get_request(url)
44+
all_group_set_items = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
45+
return all_group_set_items[0]
46+
47+
@api(version="3.22")
48+
def create(self, groupset_item: GroupSetItem) -> GroupSetItem:
49+
logger.info(f"Creating group set (name: {groupset_item.name})")
50+
url = self.baseurl
51+
request = RequestFactory.GroupSet.create_request(groupset_item)
52+
server_response = self.post_request(url, request)
53+
created_groupset = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
54+
return created_groupset[0]
55+
56+
@api(version="3.22")
57+
def add_group(self, groupset_item: GroupSetItem, group: Union[GroupItem, str]) -> None:
58+
group_id = group.id if isinstance(group, GroupItem) else group
59+
logger.info(f"Adding group (ID: {group_id}) to group set (ID: {groupset_item.id})")
60+
url = f"{self.baseurl}/{groupset_item.id}/groups/{group_id}"
61+
_ = self.put_request(url)
62+
return None
63+
64+
@api(version="3.22")
65+
def remove_group(self, groupset_item: GroupSetItem, group: Union[GroupItem, str]) -> None:
66+
group_id = group.id if isinstance(group, GroupItem) else group
67+
logger.info(f"Removing group (ID: {group_id}) from group set (ID: {groupset_item.id})")
68+
url = f"{self.baseurl}/{groupset_item.id}/groups/{group_id}"
69+
_ = self.delete_request(url)
70+
return None
71+
72+
@api(version="3.22")
73+
def delete(self, groupset: Union[GroupSetItem, str]) -> None:
74+
groupset_id = groupset.id if isinstance(groupset, GroupSetItem) else groupset
75+
logger.info(f"Deleting group set (ID: {groupset_id})")
76+
url = f"{self.baseurl}/{groupset_id}"
77+
_ = self.delete_request(url)
78+
return None
79+
80+
@api(version="3.22")
81+
def update(self, groupset: GroupSetItem) -> GroupSetItem:
82+
logger.info(f"Updating group set (ID: {groupset.id})")
83+
url = f"{self.baseurl}/{groupset.id}"
84+
request = RequestFactory.GroupSet.update_request(groupset)
85+
server_response = self.put_request(url, request)
86+
updated_groupset = GroupSetItem.from_response(server_response.content, self.parent_srv.namespace)
87+
return updated_groupset[0]

tableauserverclient/server/request_factory.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,22 @@ def update_req(self, xml_request: ET.Element, custom_view_item: CustomViewItem):
12681268
updating_element.attrib["name"] = custom_view_item.name
12691269

12701270

1271+
class GroupSetRequest:
1272+
@_tsrequest_wrapped
1273+
def create_request(self, xml_request: ET.Element, group_set_item: "GroupSetItem") -> bytes:
1274+
group_set_element = ET.SubElement(xml_request, "groupSet")
1275+
if group_set_item.name is not None:
1276+
group_set_element.attrib["name"] = group_set_item.name
1277+
return ET.tostring(xml_request)
1278+
1279+
@_tsrequest_wrapped
1280+
def update_request(self, xml_request: ET.Element, group_set_item: "GroupSetItem") -> bytes:
1281+
group_set_element = ET.SubElement(xml_request, "groupSet")
1282+
if group_set_item.name is not None:
1283+
group_set_element.attrib["name"] = group_set_item.name
1284+
return ET.tostring(xml_request)
1285+
1286+
12711287
class RequestFactory(object):
12721288
Auth = AuthRequest()
12731289
Connection = Connection()
@@ -1283,6 +1299,7 @@ class RequestFactory(object):
12831299
Flow = FlowRequest()
12841300
FlowTask = FlowTaskRequest()
12851301
Group = GroupRequest()
1302+
GroupSet = GroupSetRequest()
12861303
Metric = MetricRequest()
12871304
Permission = PermissionRequest()
12881305
Project = ProjectRequest()

tableauserverclient/server/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Metrics,
3434
Endpoint,
3535
CustomViews,
36+
GroupSets,
3637
)
3738
from tableauserverclient.server.exceptions import (
3839
ServerInfoEndpointNotFoundError,
@@ -99,6 +100,7 @@ def __init__(self, server_address, use_server_version=False, http_options=None,
99100
self.flow_runs = FlowRuns(self)
100101
self.metrics = Metrics(self)
101102
self.custom_views = CustomViews(self)
103+
self.group_sets = GroupSets(self)
102104

103105
self._session = self._session_factory()
104106
self._http_options = dict() # must set this before making a server call

test/assets/groupsets_create.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<groupSet id="1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" name="All Users" groupCount="0" />
4+
</tsResponse>

test/assets/groupsets_get.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<pagination pageNumber="1" pageSize="100" totalAvailable="3"/>
4+
<groupSets>
5+
<groupSet id="1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" name="All Users" groupCount="1">
6+
<group id="gs-1" name="group-one"/>
7+
</groupSet>
8+
<groupSet id="9a8a7b6b-5c4c-3d2d-1e0e-9a8a7b6b5b4b" name="active-directory-group-import" groupCount="1">
9+
<group id="gs21" name="group-two"/>
10+
</groupSet>
11+
<groupSet id="7b6b59a8-ac3c-4d1d-2e9e-0b5b4ba8a7b6" name="local-group-license-on-login" groupCount="1">
12+
<group id="gs-3" name="group-three"/>
13+
</groupSet>
14+
</groupSets>
15+
</tsResponse>

test/assets/groupsets_get_by_id.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<pagination pageNumber="1" pageSize="100" totalAvailable="3"/>
4+
<groupSet id="1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" name="All Users" groupCount="3">
5+
<group id="gs-1" name="group-one"/>
6+
<group id="gs21" name="group-two"/>
7+
<group id="gs-3" name="group-three"/>
8+
</groupSet>
9+
</tsResponse>

test/assets/groupsets_update.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<pagination pageNumber="1" pageSize="100" totalAvailable="3"/>
4+
<groupSet id="1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d" name="All Users" groupCount="1">
5+
<group id="gs-1" name="group-one"/>
6+
<group id="gs21" name="group-two"/>
7+
<group id="gs-3" name="group-three"/>
8+
</groupSet>
9+
</tsResponse>

0 commit comments

Comments
 (0)
0