tableauserverclient 0.38__py3-none-any.whl → 0.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _version.py +21 -0
- tableauserverclient/__init__.py +10 -5
- tableauserverclient/bin/__init__.py +3 -0
- tableauserverclient/bin/_version.py +3 -3
- tableauserverclient/models/__init__.py +9 -0
- tableauserverclient/models/collection_item.py +52 -0
- tableauserverclient/models/connection_item.py +16 -2
- tableauserverclient/models/custom_view_item.py +8 -0
- tableauserverclient/models/data_freshness_policy_item.py +3 -3
- tableauserverclient/models/datasource_item.py +3 -1
- tableauserverclient/models/extensions_item.py +186 -0
- tableauserverclient/models/favorites_item.py +21 -8
- tableauserverclient/models/flow_item.py +1 -1
- tableauserverclient/models/group_item.py +7 -1
- tableauserverclient/models/groupset_item.py +14 -0
- tableauserverclient/models/interval_item.py +2 -1
- tableauserverclient/models/oidc_item.py +82 -0
- tableauserverclient/models/permissions_item.py +2 -0
- tableauserverclient/models/project_item.py +3 -2
- tableauserverclient/models/property_decorators.py +2 -2
- tableauserverclient/models/reference_item.py +12 -6
- tableauserverclient/models/schedule_item.py +10 -1
- tableauserverclient/models/site_item.py +26 -0
- tableauserverclient/models/tableau_auth.py +13 -6
- tableauserverclient/models/user_item.py +10 -3
- tableauserverclient/models/workbook_item.py +2 -2
- tableauserverclient/server/endpoint/__init__.py +4 -0
- tableauserverclient/server/endpoint/datasources_endpoint.py +152 -22
- tableauserverclient/server/endpoint/extensions_endpoint.py +79 -0
- tableauserverclient/server/endpoint/flow_task_endpoint.py +1 -1
- tableauserverclient/server/endpoint/flows_endpoint.py +5 -4
- tableauserverclient/server/endpoint/oidc_endpoint.py +157 -0
- tableauserverclient/server/endpoint/projects_endpoint.py +12 -0
- tableauserverclient/server/endpoint/schedules_endpoint.py +48 -1
- tableauserverclient/server/endpoint/users_endpoint.py +274 -5
- tableauserverclient/server/endpoint/views_endpoint.py +23 -0
- tableauserverclient/server/endpoint/workbooks_endpoint.py +124 -9
- tableauserverclient/server/request_factory.py +281 -2
- tableauserverclient/server/request_options.py +12 -2
- tableauserverclient/server/server.py +4 -0
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.40.dist-info}/METADATA +5 -26
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.40.dist-info}/RECORD +45 -39
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.40.dist-info}/WHEEL +1 -1
- tableauserverclient-0.38.dist-info/licenses/LICENSE.versioneer +0 -7
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.40.dist-info}/licenses/LICENSE +0 -0
- {tableauserverclient-0.38.dist-info → tableauserverclient-0.40.dist-info}/top_level.txt +0 -0
_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
# This file was generated by 'versioneer.py' (0.29) from
|
|
3
|
+
# revision-control system data, or from the parent directory name of an
|
|
4
|
+
# unpacked source archive. Distribution tarballs contain a pre-generated copy
|
|
5
|
+
# of this file.
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
version_json = '''
|
|
10
|
+
{
|
|
11
|
+
"date": "2026-02-03T12:18:13-0800",
|
|
12
|
+
"dirty": false,
|
|
13
|
+
"error": null,
|
|
14
|
+
"full-revisionid": "d4b27f42d897883f8b8d0cb5b7edb558df2cbfb6",
|
|
15
|
+
"version": "0.40"
|
|
16
|
+
}
|
|
17
|
+
''' # END VERSION_JSON
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_versions():
|
|
21
|
+
return json.loads(version_json)
|
tableauserverclient/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@ from tableauserverclient.bin._version import get_versions
|
|
|
2
2
|
from tableauserverclient.namespace import NEW_NAMESPACE as DEFAULT_NAMESPACE
|
|
3
3
|
from tableauserverclient.models import (
|
|
4
4
|
BackgroundJobItem,
|
|
5
|
+
CollectionItem,
|
|
5
6
|
ColumnItem,
|
|
6
7
|
ConnectionCredentials,
|
|
7
8
|
ConnectionItem,
|
|
@@ -12,6 +13,8 @@ from tableauserverclient.models import (
|
|
|
12
13
|
DatabaseItem,
|
|
13
14
|
DataFreshnessPolicyItem,
|
|
14
15
|
DatasourceItem,
|
|
16
|
+
ExtensionsServer,
|
|
17
|
+
ExtensionsSiteSettings,
|
|
15
18
|
FavoriteItem,
|
|
16
19
|
FlowItem,
|
|
17
20
|
FlowRunItem,
|
|
@@ -35,8 +38,10 @@ from tableauserverclient.models import (
|
|
|
35
38
|
ProjectItem,
|
|
36
39
|
Resource,
|
|
37
40
|
RevisionItem,
|
|
41
|
+
SafeExtension,
|
|
38
42
|
ScheduleItem,
|
|
39
43
|
SiteAuthConfiguration,
|
|
44
|
+
SiteOIDCConfiguration,
|
|
40
45
|
SiteItem,
|
|
41
46
|
ServerInfoItem,
|
|
42
47
|
SubscriptionItem,
|
|
@@ -72,7 +77,7 @@ from tableauserverclient.server import (
|
|
|
72
77
|
|
|
73
78
|
__all__ = [
|
|
74
79
|
"BackgroundJobItem",
|
|
75
|
-
"
|
|
80
|
+
"CollectionItem",
|
|
76
81
|
"ColumnItem",
|
|
77
82
|
"ConnectionCredentials",
|
|
78
83
|
"ConnectionItem",
|
|
@@ -86,6 +91,8 @@ __all__ = [
|
|
|
86
91
|
"DEFAULT_NAMESPACE",
|
|
87
92
|
"DQWItem",
|
|
88
93
|
"ExcelRequestOptions",
|
|
94
|
+
"ExtensionsServer",
|
|
95
|
+
"ExtensionsSiteSettings",
|
|
89
96
|
"FailedSignInError",
|
|
90
97
|
"FavoriteItem",
|
|
91
98
|
"FileuploadItem",
|
|
@@ -119,12 +126,14 @@ __all__ = [
|
|
|
119
126
|
"RequestOptions",
|
|
120
127
|
"Resource",
|
|
121
128
|
"RevisionItem",
|
|
129
|
+
"SafeExtension",
|
|
122
130
|
"ScheduleItem",
|
|
123
131
|
"Server",
|
|
124
132
|
"ServerInfoItem",
|
|
125
133
|
"ServerResponseError",
|
|
126
134
|
"SiteItem",
|
|
127
135
|
"SiteAuthConfiguration",
|
|
136
|
+
"SiteOIDCConfiguration",
|
|
128
137
|
"Sort",
|
|
129
138
|
"SubscriptionItem",
|
|
130
139
|
"TableauAuth",
|
|
@@ -139,7 +148,3 @@ __all__ = [
|
|
|
139
148
|
"WeeklyInterval",
|
|
140
149
|
"WorkbookItem",
|
|
141
150
|
]
|
|
142
|
-
|
|
143
|
-
from .bin import _version
|
|
144
|
-
|
|
145
|
-
__version__ = _version.get_versions()["version"]
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2026-02-03T12:18:13-0800",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.
|
|
14
|
+
"full-revisionid": "d4b27f42d897883f8b8d0cb5b7edb558df2cbfb6",
|
|
15
|
+
"version": "0.40"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from tableauserverclient.models.collection_item import CollectionItem
|
|
1
2
|
from tableauserverclient.models.column_item import ColumnItem
|
|
2
3
|
from tableauserverclient.models.connection_credentials import ConnectionCredentials
|
|
3
4
|
from tableauserverclient.models.connection_item import ConnectionItem
|
|
@@ -9,6 +10,7 @@ from tableauserverclient.models.data_freshness_policy_item import DataFreshnessP
|
|
|
9
10
|
from tableauserverclient.models.datasource_item import DatasourceItem
|
|
10
11
|
from tableauserverclient.models.dqw_item import DQWItem
|
|
11
12
|
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
|
|
13
|
+
from tableauserverclient.models.extensions_item import ExtensionsServer, ExtensionsSiteSettings, SafeExtension
|
|
12
14
|
from tableauserverclient.models.favorites_item import FavoriteItem
|
|
13
15
|
from tableauserverclient.models.fileupload_item import FileuploadItem
|
|
14
16
|
from tableauserverclient.models.flow_item import FlowItem
|
|
@@ -30,6 +32,7 @@ from tableauserverclient.models.linked_tasks_item import (
|
|
|
30
32
|
)
|
|
31
33
|
from tableauserverclient.models.location_item import LocationItem
|
|
32
34
|
from tableauserverclient.models.metric_item import MetricItem
|
|
35
|
+
from tableauserverclient.models.oidc_item import SiteOIDCConfiguration
|
|
33
36
|
from tableauserverclient.models.pagination_item import PaginationItem
|
|
34
37
|
from tableauserverclient.models.permissions_item import PermissionsRule, Permission
|
|
35
38
|
from tableauserverclient.models.project_item import ProjectItem
|
|
@@ -52,6 +55,7 @@ from tableauserverclient.models.workbook_item import WorkbookItem
|
|
|
52
55
|
from tableauserverclient.models.extract_item import ExtractItem
|
|
53
56
|
|
|
54
57
|
__all__ = [
|
|
58
|
+
"CollectionItem",
|
|
55
59
|
"ColumnItem",
|
|
56
60
|
"ConnectionCredentials",
|
|
57
61
|
"ConnectionItem",
|
|
@@ -79,6 +83,7 @@ __all__ = [
|
|
|
79
83
|
"BackgroundJobItem",
|
|
80
84
|
"LocationItem",
|
|
81
85
|
"MetricItem",
|
|
86
|
+
"SiteOIDCConfiguration",
|
|
82
87
|
"PaginationItem",
|
|
83
88
|
"Permission",
|
|
84
89
|
"PermissionsRule",
|
|
@@ -88,6 +93,7 @@ __all__ = [
|
|
|
88
93
|
"ServerInfoItem",
|
|
89
94
|
"SiteAuthConfiguration",
|
|
90
95
|
"SiteItem",
|
|
96
|
+
"SiteOIDCConfiguration",
|
|
91
97
|
"SubscriptionItem",
|
|
92
98
|
"TableItem",
|
|
93
99
|
"TableauAuth",
|
|
@@ -108,4 +114,7 @@ __all__ = [
|
|
|
108
114
|
"LinkedTaskStepItem",
|
|
109
115
|
"LinkedTaskFlowRunItem",
|
|
110
116
|
"ExtractItem",
|
|
117
|
+
"ExtensionsServer",
|
|
118
|
+
"ExtensionsSiteSettings",
|
|
119
|
+
"SafeExtension",
|
|
111
120
|
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from xml.etree.ElementTree import Element
|
|
4
|
+
|
|
5
|
+
from defusedxml.ElementTree import fromstring
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
from tableauserverclient.datetime_helpers import parse_datetime
|
|
9
|
+
from tableauserverclient.models.user_item import UserItem
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CollectionItem:
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self.id: Optional[str] = None
|
|
15
|
+
self.name: Optional[str] = None
|
|
16
|
+
self.description: Optional[str] = None
|
|
17
|
+
self.created_at: Optional[datetime] = None
|
|
18
|
+
self.updated_at: Optional[datetime] = None
|
|
19
|
+
self.owner: Optional[UserItem] = None
|
|
20
|
+
self.total_item_count: Optional[int] = None
|
|
21
|
+
self.permissioned_item_count: Optional[int] = None
|
|
22
|
+
self.visibility: Optional[str] = None # Assuming visibility is a string, adjust as necessary
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_response(cls, response: bytes, ns) -> list[Self]:
|
|
26
|
+
parsed_response = fromstring(response)
|
|
27
|
+
|
|
28
|
+
collection_elements = parsed_response.findall(".//t:collection", namespaces=ns)
|
|
29
|
+
if not collection_elements:
|
|
30
|
+
raise ValueError("No collection element found in the response")
|
|
31
|
+
|
|
32
|
+
collections = [cls.from_xml(c, ns) for c in collection_elements]
|
|
33
|
+
return collections
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_xml(cls, xml: Element, ns) -> Self:
|
|
37
|
+
collection_item = cls()
|
|
38
|
+
collection_item.id = xml.get("id")
|
|
39
|
+
collection_item.name = xml.get("name")
|
|
40
|
+
collection_item.description = xml.get("description")
|
|
41
|
+
collection_item.created_at = parse_datetime(xml.get("createdAt"))
|
|
42
|
+
collection_item.updated_at = parse_datetime(xml.get("updatedAt"))
|
|
43
|
+
owner_element = xml.find(".//t:owner", namespaces=ns)
|
|
44
|
+
if owner_element is not None:
|
|
45
|
+
collection_item.owner = UserItem.from_xml(owner_element, ns)
|
|
46
|
+
else:
|
|
47
|
+
collection_item.owner = None
|
|
48
|
+
collection_item.total_item_count = int(xml.get("totalItemCount", 0))
|
|
49
|
+
collection_item.permissioned_item_count = int(xml.get("permissionedItemCount", 0))
|
|
50
|
+
collection_item.visibility = xml.get("visibility")
|
|
51
|
+
|
|
52
|
+
return collection_item
|
|
@@ -41,6 +41,9 @@ class ConnectionItem:
|
|
|
41
41
|
server_port: str
|
|
42
42
|
The port used for the connection.
|
|
43
43
|
|
|
44
|
+
auth_type: str
|
|
45
|
+
Specifies the type of authentication used by the connection.
|
|
46
|
+
|
|
44
47
|
connection_credentials: ConnectionCredentials
|
|
45
48
|
The Connection Credentials object containing authentication details for
|
|
46
49
|
the connection. Replaces username/password/embed_password when
|
|
@@ -59,6 +62,7 @@ class ConnectionItem:
|
|
|
59
62
|
self.username: Optional[str] = None
|
|
60
63
|
self.connection_credentials: Optional[ConnectionCredentials] = None
|
|
61
64
|
self._query_tagging: Optional[bool] = None
|
|
65
|
+
self._auth_type: Optional[str] = None
|
|
62
66
|
|
|
63
67
|
@property
|
|
64
68
|
def datasource_id(self) -> Optional[str]:
|
|
@@ -91,8 +95,16 @@ class ConnectionItem:
|
|
|
91
95
|
return
|
|
92
96
|
self._query_tagging = value
|
|
93
97
|
|
|
98
|
+
@property
|
|
99
|
+
def auth_type(self) -> Optional[str]:
|
|
100
|
+
return self._auth_type
|
|
101
|
+
|
|
102
|
+
@auth_type.setter
|
|
103
|
+
def auth_type(self, value: Optional[str]):
|
|
104
|
+
self._auth_type = value
|
|
105
|
+
|
|
94
106
|
def __repr__(self):
|
|
95
|
-
return "<ConnectionItem#{_id} embed={embed_password} type={_connection_type} username={username}>".format(
|
|
107
|
+
return "<ConnectionItem#{_id} embed={embed_password} type={_connection_type} auth={_auth_type} username={username}>".format(
|
|
96
108
|
**self.__dict__
|
|
97
109
|
)
|
|
98
110
|
|
|
@@ -108,10 +120,11 @@ class ConnectionItem:
|
|
|
108
120
|
connection_item.embed_password = string_to_bool(connection_xml.get("embedPassword", ""))
|
|
109
121
|
connection_item.server_address = connection_xml.get("serverAddress", connection_xml.get("server", None))
|
|
110
122
|
connection_item.server_port = connection_xml.get("serverPort", connection_xml.get("port", None))
|
|
111
|
-
connection_item.username = connection_xml.get("userName", None)
|
|
123
|
+
connection_item.username = connection_xml.get("userName", connection_xml.get("username", None))
|
|
112
124
|
connection_item._query_tagging = (
|
|
113
125
|
string_to_bool(s) if (s := connection_xml.get("queryTagging", None)) else None
|
|
114
126
|
)
|
|
127
|
+
connection_item._auth_type = connection_xml.get("authenticationType", None)
|
|
115
128
|
datasource_elem = connection_xml.find(".//t:datasource", namespaces=ns)
|
|
116
129
|
if datasource_elem is not None:
|
|
117
130
|
connection_item._datasource_id = datasource_elem.get("id", None)
|
|
@@ -139,6 +152,7 @@ class ConnectionItem:
|
|
|
139
152
|
|
|
140
153
|
connection_item.server_address = connection_xml.get("serverAddress", None)
|
|
141
154
|
connection_item.server_port = connection_xml.get("serverPort", None)
|
|
155
|
+
connection_item._auth_type = connection_xml.get("authenticationType", None)
|
|
142
156
|
|
|
143
157
|
connection_credentials = connection_xml.find(".//t:connectionCredentials", namespaces=ns)
|
|
144
158
|
|
|
@@ -158,10 +158,18 @@ class CustomViewItem:
|
|
|
158
158
|
def workbook(self) -> Optional[WorkbookItem]:
|
|
159
159
|
return self._workbook
|
|
160
160
|
|
|
161
|
+
@workbook.setter
|
|
162
|
+
def workbook(self, value: WorkbookItem) -> None:
|
|
163
|
+
self._workbook = value
|
|
164
|
+
|
|
161
165
|
@property
|
|
162
166
|
def view(self) -> Optional[ViewItem]:
|
|
163
167
|
return self._view
|
|
164
168
|
|
|
169
|
+
@view.setter
|
|
170
|
+
def view(self, value: ViewItem) -> None:
|
|
171
|
+
self._view = value
|
|
172
|
+
|
|
165
173
|
@classmethod
|
|
166
174
|
def from_response(cls, resp, ns, workbook_id="") -> Optional["CustomViewItem"]:
|
|
167
175
|
item = cls.list_from_response(resp, ns, workbook_id)
|
|
@@ -66,7 +66,7 @@ class DataFreshnessPolicyItem:
|
|
|
66
66
|
return self._interval_item
|
|
67
67
|
|
|
68
68
|
@interval_item.setter
|
|
69
|
-
def interval_item(self, value: list[str]):
|
|
69
|
+
def interval_item(self, value: Optional[list[str]]):
|
|
70
70
|
self._interval_item = value
|
|
71
71
|
|
|
72
72
|
@property
|
|
@@ -127,7 +127,7 @@ class DataFreshnessPolicyItem:
|
|
|
127
127
|
return self._fresh_every_schedule
|
|
128
128
|
|
|
129
129
|
@fresh_every_schedule.setter
|
|
130
|
-
def fresh_every_schedule(self, value: FreshEvery):
|
|
130
|
+
def fresh_every_schedule(self, value: Optional[FreshEvery]):
|
|
131
131
|
self._fresh_every_schedule = value
|
|
132
132
|
|
|
133
133
|
@property
|
|
@@ -135,7 +135,7 @@ class DataFreshnessPolicyItem:
|
|
|
135
135
|
return self._fresh_at_schedule
|
|
136
136
|
|
|
137
137
|
@fresh_at_schedule.setter
|
|
138
|
-
def fresh_at_schedule(self, value: FreshAt):
|
|
138
|
+
def fresh_at_schedule(self, value: Optional[FreshAt]):
|
|
139
139
|
self._fresh_at_schedule = value
|
|
140
140
|
|
|
141
141
|
@classmethod
|
|
@@ -490,7 +490,7 @@ class DatasourceItem:
|
|
|
490
490
|
self._owner = owner
|
|
491
491
|
|
|
492
492
|
@classmethod
|
|
493
|
-
def from_response(cls, resp:
|
|
493
|
+
def from_response(cls, resp: bytes, ns: dict) -> list["DatasourceItem"]:
|
|
494
494
|
all_datasource_items = list()
|
|
495
495
|
parsed_response = fromstring(resp)
|
|
496
496
|
all_datasource_xml = parsed_response.findall(".//t:datasource", namespaces=ns)
|
|
@@ -535,6 +535,7 @@ class DatasourceItem:
|
|
|
535
535
|
|
|
536
536
|
project_id = None
|
|
537
537
|
project_name = None
|
|
538
|
+
project = None
|
|
538
539
|
project_elem = datasource_xml.find(".//t:project", namespaces=ns)
|
|
539
540
|
if project_elem is not None:
|
|
540
541
|
project = ProjectItem.from_xml(project_elem, ns)
|
|
@@ -542,6 +543,7 @@ class DatasourceItem:
|
|
|
542
543
|
project_name = project_elem.get("name", None)
|
|
543
544
|
|
|
544
545
|
owner_id = None
|
|
546
|
+
owner = None
|
|
545
547
|
owner_elem = datasource_xml.find(".//t:owner", namespaces=ns)
|
|
546
548
|
if owner_elem is not None:
|
|
547
549
|
owner = UserItem.from_xml(owner_elem, ns)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from typing import overload
|
|
2
|
+
from typing_extensions import Self
|
|
3
|
+
|
|
4
|
+
from defusedxml.ElementTree import fromstring
|
|
5
|
+
|
|
6
|
+
from tableauserverclient.models.property_decorators import property_is_boolean
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ExtensionsServer:
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
self._enabled: bool | None = None
|
|
12
|
+
self._block_list: list[str] | None = None
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def enabled(self) -> bool | None:
|
|
16
|
+
"""Indicates whether the extensions server is enabled."""
|
|
17
|
+
return self._enabled
|
|
18
|
+
|
|
19
|
+
@enabled.setter
|
|
20
|
+
@property_is_boolean
|
|
21
|
+
def enabled(self, value: bool | None) -> None:
|
|
22
|
+
self._enabled = value
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def block_list(self) -> list[str] | None:
|
|
26
|
+
"""List of blocked extensions."""
|
|
27
|
+
return self._block_list
|
|
28
|
+
|
|
29
|
+
@block_list.setter
|
|
30
|
+
def block_list(self, value: list[str] | None) -> None:
|
|
31
|
+
self._block_list = value
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def from_response(cls: type[Self], response, ns) -> Self:
|
|
35
|
+
xml = fromstring(response)
|
|
36
|
+
obj = cls()
|
|
37
|
+
element = xml.find(".//t:extensionsServerSettings", namespaces=ns)
|
|
38
|
+
if element is None:
|
|
39
|
+
raise ValueError("Missing extensionsServerSettings element in response")
|
|
40
|
+
|
|
41
|
+
if (enabled_element := element.find("./t:extensionsGloballyEnabled", namespaces=ns)) is not None:
|
|
42
|
+
obj.enabled = string_to_bool(enabled_element.text)
|
|
43
|
+
obj.block_list = [e.text for e in element.findall("./t:blockList", namespaces=ns) if e.text is not None]
|
|
44
|
+
|
|
45
|
+
return obj
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SafeExtension:
|
|
49
|
+
def __init__(
|
|
50
|
+
self, url: str | None = None, full_data_allowed: bool | None = None, prompt_needed: bool | None = None
|
|
51
|
+
) -> None:
|
|
52
|
+
self.url = url
|
|
53
|
+
self._full_data_allowed = full_data_allowed
|
|
54
|
+
self._prompt_needed = prompt_needed
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def full_data_allowed(self) -> bool | None:
|
|
58
|
+
return self._full_data_allowed
|
|
59
|
+
|
|
60
|
+
@full_data_allowed.setter
|
|
61
|
+
@property_is_boolean
|
|
62
|
+
def full_data_allowed(self, value: bool | None) -> None:
|
|
63
|
+
self._full_data_allowed = value
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def prompt_needed(self) -> bool | None:
|
|
67
|
+
return self._prompt_needed
|
|
68
|
+
|
|
69
|
+
@prompt_needed.setter
|
|
70
|
+
@property_is_boolean
|
|
71
|
+
def prompt_needed(self, value: bool | None) -> None:
|
|
72
|
+
self._prompt_needed = value
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ExtensionsSiteSettings:
|
|
76
|
+
def __init__(self) -> None:
|
|
77
|
+
self._enabled: bool | None = None
|
|
78
|
+
self._use_default_setting: bool | None = None
|
|
79
|
+
self.safe_list: list[SafeExtension] | None = None
|
|
80
|
+
self._allow_trusted: bool | None = None
|
|
81
|
+
self._include_tableau_built: bool | None = None
|
|
82
|
+
self._include_partner_built: bool | None = None
|
|
83
|
+
self._include_sandboxed: bool | None = None
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def enabled(self) -> bool | None:
|
|
87
|
+
return self._enabled
|
|
88
|
+
|
|
89
|
+
@enabled.setter
|
|
90
|
+
@property_is_boolean
|
|
91
|
+
def enabled(self, value: bool | None) -> None:
|
|
92
|
+
self._enabled = value
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def use_default_setting(self) -> bool | None:
|
|
96
|
+
return self._use_default_setting
|
|
97
|
+
|
|
98
|
+
@use_default_setting.setter
|
|
99
|
+
@property_is_boolean
|
|
100
|
+
def use_default_setting(self, value: bool | None) -> None:
|
|
101
|
+
self._use_default_setting = value
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def allow_trusted(self) -> bool | None:
|
|
105
|
+
return self._allow_trusted
|
|
106
|
+
|
|
107
|
+
@allow_trusted.setter
|
|
108
|
+
@property_is_boolean
|
|
109
|
+
def allow_trusted(self, value: bool | None) -> None:
|
|
110
|
+
self._allow_trusted = value
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def include_tableau_built(self) -> bool | None:
|
|
114
|
+
return self._include_tableau_built
|
|
115
|
+
|
|
116
|
+
@include_tableau_built.setter
|
|
117
|
+
@property_is_boolean
|
|
118
|
+
def include_tableau_built(self, value: bool | None) -> None:
|
|
119
|
+
self._include_tableau_built = value
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def include_partner_built(self) -> bool | None:
|
|
123
|
+
return self._include_partner_built
|
|
124
|
+
|
|
125
|
+
@include_partner_built.setter
|
|
126
|
+
@property_is_boolean
|
|
127
|
+
def include_partner_built(self, value: bool | None) -> None:
|
|
128
|
+
self._include_partner_built = value
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def include_sandboxed(self) -> bool | None:
|
|
132
|
+
return self._include_sandboxed
|
|
133
|
+
|
|
134
|
+
@include_sandboxed.setter
|
|
135
|
+
@property_is_boolean
|
|
136
|
+
def include_sandboxed(self, value: bool | None) -> None:
|
|
137
|
+
self._include_sandboxed = value
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def from_response(cls: type[Self], response, ns) -> Self:
|
|
141
|
+
xml = fromstring(response)
|
|
142
|
+
element = xml.find(".//t:extensionsSiteSettings", namespaces=ns)
|
|
143
|
+
obj = cls()
|
|
144
|
+
if element is None:
|
|
145
|
+
raise ValueError("Missing extensionsSiteSettings element in response")
|
|
146
|
+
|
|
147
|
+
if (enabled_element := element.find("./t:extensionsEnabled", namespaces=ns)) is not None:
|
|
148
|
+
obj.enabled = string_to_bool(enabled_element.text)
|
|
149
|
+
if (default_settings_element := element.find("./t:useDefaultSetting", namespaces=ns)) is not None:
|
|
150
|
+
obj.use_default_setting = string_to_bool(default_settings_element.text)
|
|
151
|
+
if (allow_trusted_element := element.find("./t:allowTrusted", namespaces=ns)) is not None:
|
|
152
|
+
obj.allow_trusted = string_to_bool(allow_trusted_element.text)
|
|
153
|
+
if (include_tableau_built_element := element.find("./t:includeTableauBuilt", namespaces=ns)) is not None:
|
|
154
|
+
obj.include_tableau_built = string_to_bool(include_tableau_built_element.text)
|
|
155
|
+
if (include_partner_built_element := element.find("./t:includePartnerBuilt", namespaces=ns)) is not None:
|
|
156
|
+
obj.include_partner_built = string_to_bool(include_partner_built_element.text)
|
|
157
|
+
if (include_sandboxed_element := element.find("./t:includeSandboxed", namespaces=ns)) is not None:
|
|
158
|
+
obj.include_sandboxed = string_to_bool(include_sandboxed_element.text)
|
|
159
|
+
|
|
160
|
+
safe_list = []
|
|
161
|
+
for safe_extension_element in element.findall("./t:safeList", namespaces=ns):
|
|
162
|
+
url = safe_extension_element.find("./t:url", namespaces=ns)
|
|
163
|
+
full_data_allowed = safe_extension_element.find("./t:fullDataAllowed", namespaces=ns)
|
|
164
|
+
prompt_needed = safe_extension_element.find("./t:promptNeeded", namespaces=ns)
|
|
165
|
+
|
|
166
|
+
safe_extension = SafeExtension(
|
|
167
|
+
url=url.text if url is not None else None,
|
|
168
|
+
full_data_allowed=string_to_bool(full_data_allowed.text) if full_data_allowed is not None else None,
|
|
169
|
+
prompt_needed=string_to_bool(prompt_needed.text) if prompt_needed is not None else None,
|
|
170
|
+
)
|
|
171
|
+
safe_list.append(safe_extension)
|
|
172
|
+
|
|
173
|
+
obj.safe_list = safe_list
|
|
174
|
+
return obj
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@overload
|
|
178
|
+
def string_to_bool(s: str) -> bool: ...
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@overload
|
|
182
|
+
def string_to_bool(s: None) -> None: ...
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def string_to_bool(s):
|
|
186
|
+
return s.lower() == "true" if s is not None else None
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
-
from typing import Union
|
|
3
|
+
from typing import TypedDict, Union
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
|
-
|
|
6
|
-
from tableauserverclient.models.tableau_types import TableauItem
|
|
5
|
+
from tableauserverclient.models.collection_item import CollectionItem
|
|
7
6
|
from tableauserverclient.models.datasource_item import DatasourceItem
|
|
8
7
|
from tableauserverclient.models.flow_item import FlowItem
|
|
9
8
|
from tableauserverclient.models.project_item import ProjectItem
|
|
@@ -13,16 +12,22 @@ from tableauserverclient.models.workbook_item import WorkbookItem
|
|
|
13
12
|
|
|
14
13
|
from tableauserverclient.helpers.logging import logger
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
list[
|
|
19
|
-
]
|
|
15
|
+
|
|
16
|
+
class FavoriteType(TypedDict):
|
|
17
|
+
collections: list[CollectionItem]
|
|
18
|
+
datasources: list[DatasourceItem]
|
|
19
|
+
flows: list[FlowItem]
|
|
20
|
+
projects: list[ProjectItem]
|
|
21
|
+
metrics: list[MetricItem]
|
|
22
|
+
views: list[ViewItem]
|
|
23
|
+
workbooks: list[WorkbookItem]
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
class FavoriteItem:
|
|
23
27
|
@classmethod
|
|
24
28
|
def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
|
|
25
29
|
favorites: FavoriteType = {
|
|
30
|
+
"collections": [],
|
|
26
31
|
"datasources": [],
|
|
27
32
|
"flows": [],
|
|
28
33
|
"projects": [],
|
|
@@ -32,6 +37,7 @@ class FavoriteItem:
|
|
|
32
37
|
}
|
|
33
38
|
parsed_response = fromstring(xml)
|
|
34
39
|
|
|
40
|
+
collections_xml = parsed_response.findall(".//t:favorite/t:collection", namespace)
|
|
35
41
|
datasources_xml = parsed_response.findall(".//t:favorite/t:datasource", namespace)
|
|
36
42
|
flows_xml = parsed_response.findall(".//t:favorite/t:flow", namespace)
|
|
37
43
|
metrics_xml = parsed_response.findall(".//t:favorite/t:metric", namespace)
|
|
@@ -40,13 +46,14 @@ class FavoriteItem:
|
|
|
40
46
|
workbooks_xml = parsed_response.findall(".//t:favorite/t:workbook", namespace)
|
|
41
47
|
|
|
42
48
|
logger.debug(
|
|
43
|
-
"ds: {}, flows: {}, metrics: {}, projects: {}, views: {}, wbs: {}".format(
|
|
49
|
+
"ds: {}, flows: {}, metrics: {}, projects: {}, views: {}, wbs: {}, collections: {}".format(
|
|
44
50
|
len(datasources_xml),
|
|
45
51
|
len(flows_xml),
|
|
46
52
|
len(metrics_xml),
|
|
47
53
|
len(projects_xml),
|
|
48
54
|
len(views_xml),
|
|
49
55
|
len(workbooks_xml),
|
|
56
|
+
len(collections_xml),
|
|
50
57
|
)
|
|
51
58
|
)
|
|
52
59
|
for datasource in datasources_xml:
|
|
@@ -85,5 +92,11 @@ class FavoriteItem:
|
|
|
85
92
|
logger.debug(fav_workbook)
|
|
86
93
|
favorites["workbooks"].append(fav_workbook)
|
|
87
94
|
|
|
95
|
+
for collection in collections_xml:
|
|
96
|
+
fav_collection = CollectionItem.from_xml(collection, namespace)
|
|
97
|
+
if fav_collection:
|
|
98
|
+
logger.debug(fav_collection)
|
|
99
|
+
favorites["collections"].append(fav_collection)
|
|
100
|
+
|
|
88
101
|
logger.debug(favorites)
|
|
89
102
|
return favorites
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Callable, Optional, TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from defusedxml.ElementTree import fromstring
|
|
4
|
+
from typing_extensions import Self
|
|
4
5
|
|
|
5
6
|
from .exceptions import UnpopulatedPropertyError
|
|
6
7
|
from .property_decorators import property_not_empty, property_is_enum
|
|
@@ -92,7 +93,7 @@ class GroupItem:
|
|
|
92
93
|
return self._name
|
|
93
94
|
|
|
94
95
|
@name.setter
|
|
95
|
-
def name(self, value: str) -> None:
|
|
96
|
+
def name(self, value: Optional[str]) -> None:
|
|
96
97
|
self._name = value
|
|
97
98
|
|
|
98
99
|
@property
|
|
@@ -157,3 +158,8 @@ class GroupItem:
|
|
|
157
158
|
@staticmethod
|
|
158
159
|
def as_reference(id_: str) -> ResourceReference:
|
|
159
160
|
return ResourceReference(id_, GroupItem.tag_name)
|
|
161
|
+
|
|
162
|
+
def to_reference(self: Self) -> ResourceReference:
|
|
163
|
+
if self.id is None:
|
|
164
|
+
raise ValueError(f"{self.__class__.__qualname__} must have id to be converted to reference")
|
|
165
|
+
return ResourceReference(self.id, self.tag_name)
|
|
@@ -2,6 +2,7 @@ from typing import Optional
|
|
|
2
2
|
import xml.etree.ElementTree as ET
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
|
+
from typing_extensions import Self
|
|
5
6
|
|
|
6
7
|
from tableauserverclient.models.group_item import GroupItem
|
|
7
8
|
from tableauserverclient.models.reference_item import ResourceReference
|
|
@@ -24,6 +25,14 @@ class GroupSetItem:
|
|
|
24
25
|
def __repr__(self) -> str:
|
|
25
26
|
return self.__str__()
|
|
26
27
|
|
|
28
|
+
@property
|
|
29
|
+
def name(self) -> Optional[str]:
|
|
30
|
+
return self._name
|
|
31
|
+
|
|
32
|
+
@name.setter
|
|
33
|
+
def name(self, value: Optional[str]) -> None:
|
|
34
|
+
self._name = value
|
|
35
|
+
|
|
27
36
|
@classmethod
|
|
28
37
|
def from_response(cls, response: bytes, ns: dict[str, str]) -> list["GroupSetItem"]:
|
|
29
38
|
parsed_response = fromstring(response)
|
|
@@ -51,3 +60,8 @@ class GroupSetItem:
|
|
|
51
60
|
@staticmethod
|
|
52
61
|
def as_reference(id_: str) -> ResourceReference:
|
|
53
62
|
return ResourceReference(id_, GroupSetItem.tag_name)
|
|
63
|
+
|
|
64
|
+
def to_reference(self: Self) -> ResourceReference:
|
|
65
|
+
if self.id is None:
|
|
66
|
+
raise ValueError(f"{self.__class__.__qualname__} must have id to be converted to reference")
|
|
67
|
+
return ResourceReference(self.id, self.tag_name)
|