tableauserverclient 0.33__py3-none-any.whl → 0.34__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.
- tableauserverclient/__init__.py +28 -22
- tableauserverclient/_version.py +3 -3
- tableauserverclient/config.py +5 -3
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +1 -1
- tableauserverclient/models/connection_item.py +6 -6
- tableauserverclient/models/custom_view_item.py +29 -6
- tableauserverclient/models/data_acceleration_report_item.py +2 -2
- tableauserverclient/models/data_alert_item.py +5 -5
- tableauserverclient/models/data_freshness_policy_item.py +6 -6
- tableauserverclient/models/database_item.py +3 -3
- tableauserverclient/models/datasource_item.py +10 -10
- tableauserverclient/models/dqw_item.py +1 -1
- tableauserverclient/models/favorites_item.py +5 -6
- tableauserverclient/models/fileupload_item.py +1 -1
- tableauserverclient/models/flow_item.py +6 -6
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +4 -4
- tableauserverclient/models/groupset_item.py +4 -4
- tableauserverclient/models/interval_item.py +9 -9
- tableauserverclient/models/job_item.py +8 -8
- tableauserverclient/models/linked_tasks_item.py +5 -5
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +12 -10
- tableauserverclient/models/project_item.py +35 -19
- tableauserverclient/models/property_decorators.py +12 -11
- tableauserverclient/models/reference_item.py +2 -2
- tableauserverclient/models/revision_item.py +3 -3
- tableauserverclient/models/schedule_item.py +2 -2
- tableauserverclient/models/server_info_item.py +26 -6
- tableauserverclient/models/site_item.py +69 -3
- tableauserverclient/models/subscription_item.py +3 -3
- tableauserverclient/models/table_item.py +1 -1
- tableauserverclient/models/tableau_auth.py +115 -5
- tableauserverclient/models/tableau_types.py +2 -2
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +4 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +11 -10
- tableauserverclient/models/virtual_connection_item.py +6 -5
- tableauserverclient/models/webhook_item.py +6 -6
- tableauserverclient/models/workbook_item.py +90 -12
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/auth_endpoint.py +65 -8
- tableauserverclient/server/endpoint/custom_views_endpoint.py +62 -18
- tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
- tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
- tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
- tableauserverclient/server/endpoint/datasources_endpoint.py +49 -54
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +19 -21
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +30 -29
- tableauserverclient/server/endpoint/groups_endpoint.py +18 -17
- tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
- tableauserverclient/server/endpoint/jobs_endpoint.py +7 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
- tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
- tableauserverclient/server/endpoint/projects_endpoint.py +81 -30
- tableauserverclient/server/endpoint/resource_tagger.py +14 -13
- tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
- tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
- tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
- tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
- tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
- tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
- tableauserverclient/server/endpoint/users_endpoint.py +366 -19
- tableauserverclient/server/endpoint/views_endpoint.py +19 -18
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
- tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +647 -61
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +5 -6
- tableauserverclient/server/query.py +68 -19
- tableauserverclient/server/request_factory.py +37 -36
- tableauserverclient/server/request_options.py +123 -145
- tableauserverclient/server/server.py +65 -9
- tableauserverclient/server/sort.py +2 -2
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/METADATA +6 -6
- tableauserverclient-0.34.dist-info/RECORD +106 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
- tableauserverclient-0.33.dist-info/RECORD +0 -106
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
tableauserverclient/__init__.py
CHANGED
|
@@ -32,11 +32,13 @@ from tableauserverclient.models import (
|
|
|
32
32
|
PermissionsRule,
|
|
33
33
|
PersonalAccessTokenAuth,
|
|
34
34
|
ProjectItem,
|
|
35
|
+
Resource,
|
|
35
36
|
RevisionItem,
|
|
36
37
|
ScheduleItem,
|
|
37
38
|
SiteItem,
|
|
38
39
|
ServerInfoItem,
|
|
39
40
|
SubscriptionItem,
|
|
41
|
+
TableauItem,
|
|
40
42
|
TableItem,
|
|
41
43
|
TableauAuth,
|
|
42
44
|
Target,
|
|
@@ -56,6 +58,7 @@ from tableauserverclient.server import (
|
|
|
56
58
|
PDFRequestOptions,
|
|
57
59
|
RequestOptions,
|
|
58
60
|
MissingRequiredFieldError,
|
|
61
|
+
FailedSignInError,
|
|
59
62
|
NotSignedInError,
|
|
60
63
|
ServerResponseError,
|
|
61
64
|
Filter,
|
|
@@ -65,65 +68,68 @@ from tableauserverclient.server import (
|
|
|
65
68
|
)
|
|
66
69
|
|
|
67
70
|
__all__ = [
|
|
68
|
-
"get_versions",
|
|
69
|
-
"DEFAULT_NAMESPACE",
|
|
70
71
|
"BackgroundJobItem",
|
|
71
72
|
"BackgroundJobItem",
|
|
72
73
|
"ColumnItem",
|
|
73
74
|
"ConnectionCredentials",
|
|
74
75
|
"ConnectionItem",
|
|
76
|
+
"CSVRequestOptions",
|
|
75
77
|
"CustomViewItem",
|
|
76
|
-
"DQWItem",
|
|
77
78
|
"DailyInterval",
|
|
78
79
|
"DataAlertItem",
|
|
79
80
|
"DatabaseItem",
|
|
80
81
|
"DataFreshnessPolicyItem",
|
|
81
82
|
"DatasourceItem",
|
|
83
|
+
"DEFAULT_NAMESPACE",
|
|
84
|
+
"DQWItem",
|
|
85
|
+
"ExcelRequestOptions",
|
|
86
|
+
"FailedSignInError",
|
|
82
87
|
"FavoriteItem",
|
|
88
|
+
"FileuploadItem",
|
|
89
|
+
"Filter",
|
|
83
90
|
"FlowItem",
|
|
84
91
|
"FlowRunItem",
|
|
85
|
-
"
|
|
92
|
+
"get_versions",
|
|
86
93
|
"GroupItem",
|
|
87
94
|
"GroupSetItem",
|
|
88
95
|
"HourlyInterval",
|
|
96
|
+
"ImageRequestOptions",
|
|
89
97
|
"IntervalItem",
|
|
90
98
|
"JobItem",
|
|
91
99
|
"JWTAuth",
|
|
100
|
+
"LinkedTaskFlowRunItem",
|
|
101
|
+
"LinkedTaskItem",
|
|
102
|
+
"LinkedTaskStepItem",
|
|
92
103
|
"MetricItem",
|
|
104
|
+
"MissingRequiredFieldError",
|
|
93
105
|
"MonthlyInterval",
|
|
106
|
+
"NotSignedInError",
|
|
107
|
+
"Pager",
|
|
94
108
|
"PaginationItem",
|
|
109
|
+
"PDFRequestOptions",
|
|
95
110
|
"Permission",
|
|
96
111
|
"PermissionsRule",
|
|
97
112
|
"PersonalAccessTokenAuth",
|
|
98
113
|
"ProjectItem",
|
|
114
|
+
"RequestOptions",
|
|
115
|
+
"Resource",
|
|
99
116
|
"RevisionItem",
|
|
100
117
|
"ScheduleItem",
|
|
101
|
-
"
|
|
118
|
+
"Server",
|
|
102
119
|
"ServerInfoItem",
|
|
120
|
+
"ServerResponseError",
|
|
121
|
+
"SiteItem",
|
|
122
|
+
"Sort",
|
|
103
123
|
"SubscriptionItem",
|
|
104
|
-
"TableItem",
|
|
105
124
|
"TableauAuth",
|
|
125
|
+
"TableauItem",
|
|
126
|
+
"TableItem",
|
|
106
127
|
"Target",
|
|
107
128
|
"TaskItem",
|
|
108
129
|
"UserItem",
|
|
109
130
|
"ViewItem",
|
|
131
|
+
"VirtualConnectionItem",
|
|
110
132
|
"WebhookItem",
|
|
111
133
|
"WeeklyInterval",
|
|
112
134
|
"WorkbookItem",
|
|
113
|
-
"CSVRequestOptions",
|
|
114
|
-
"ExcelRequestOptions",
|
|
115
|
-
"ImageRequestOptions",
|
|
116
|
-
"PDFRequestOptions",
|
|
117
|
-
"RequestOptions",
|
|
118
|
-
"MissingRequiredFieldError",
|
|
119
|
-
"NotSignedInError",
|
|
120
|
-
"ServerResponseError",
|
|
121
|
-
"Filter",
|
|
122
|
-
"Pager",
|
|
123
|
-
"Server",
|
|
124
|
-
"Sort",
|
|
125
|
-
"LinkedTaskItem",
|
|
126
|
-
"LinkedTaskStepItem",
|
|
127
|
-
"LinkedTaskFlowRunItem",
|
|
128
|
-
"VirtualConnectionItem",
|
|
129
135
|
]
|
tableauserverclient/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-10-25T16:34:58-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.
|
|
14
|
+
"full-revisionid": "1d98fdad189ebed130fb904e8fa5dca2207f9011",
|
|
15
|
+
"version": "0.34"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
tableauserverclient/config.py
CHANGED
|
@@ -6,11 +6,13 @@ BYTES_PER_MB = 1024 * 1024
|
|
|
6
6
|
|
|
7
7
|
DELAY_SLEEP_SECONDS = 0.1
|
|
8
8
|
|
|
9
|
-
# The maximum size of a file that can be published in a single request is 64MB
|
|
10
|
-
FILESIZE_LIMIT_MB = 64
|
|
11
|
-
|
|
12
9
|
|
|
13
10
|
class Config:
|
|
11
|
+
# The maximum size of a file that can be published in a single request is 64MB
|
|
12
|
+
@property
|
|
13
|
+
def FILESIZE_LIMIT_MB(self):
|
|
14
|
+
return min(int(os.getenv("TSC_FILESIZE_LIMIT_MB", 64)), 64)
|
|
15
|
+
|
|
14
16
|
# For when a datasource is over 64MB, break it into 5MB(standard chunk size) chunks
|
|
15
17
|
@property
|
|
16
18
|
def CHUNK_SIZE_MB(self):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .property_decorators import property_is_boolean
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class ConnectionCredentials
|
|
4
|
+
class ConnectionCredentials:
|
|
5
5
|
"""Connection Credentials for Workbooks and Datasources publish request.
|
|
6
6
|
|
|
7
7
|
Consider removing this object and other variables holding secrets
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ from .property_decorators import property_is_boolean
|
|
|
8
8
|
from tableauserverclient.helpers.logging import logger
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class ConnectionItem
|
|
11
|
+
class ConnectionItem:
|
|
12
12
|
def __init__(self):
|
|
13
13
|
self._datasource_id: Optional[str] = None
|
|
14
14
|
self._datasource_name: Optional[str] = None
|
|
@@ -48,7 +48,7 @@ class ConnectionItem(object):
|
|
|
48
48
|
# if connection type = hyper, Snowflake, or Teradata, we can't change this value: it is always true
|
|
49
49
|
if self._connection_type in ["hyper", "snowflake", "teradata"]:
|
|
50
50
|
logger.debug(
|
|
51
|
-
"Cannot update value: Query tagging is always enabled for {} connections"
|
|
51
|
+
f"Cannot update value: Query tagging is always enabled for {self._connection_type} connections"
|
|
52
52
|
)
|
|
53
53
|
return
|
|
54
54
|
self._query_tagging = value
|
|
@@ -59,7 +59,7 @@ class ConnectionItem(object):
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
@classmethod
|
|
62
|
-
def from_response(cls, resp, ns) ->
|
|
62
|
+
def from_response(cls, resp, ns) -> list["ConnectionItem"]:
|
|
63
63
|
all_connection_items = list()
|
|
64
64
|
parsed_response = fromstring(resp)
|
|
65
65
|
all_connection_xml = parsed_response.findall(".//t:connection", namespaces=ns)
|
|
@@ -82,7 +82,7 @@ class ConnectionItem(object):
|
|
|
82
82
|
return all_connection_items
|
|
83
83
|
|
|
84
84
|
@classmethod
|
|
85
|
-
def from_xml_element(cls, parsed_response, ns) ->
|
|
85
|
+
def from_xml_element(cls, parsed_response, ns) -> list["ConnectionItem"]:
|
|
86
86
|
"""
|
|
87
87
|
<connections>
|
|
88
88
|
<connection serverAddress="mysql.test.com">
|
|
@@ -93,7 +93,7 @@ class ConnectionItem(object):
|
|
|
93
93
|
</connection>
|
|
94
94
|
</connections>
|
|
95
95
|
"""
|
|
96
|
-
all_connection_items:
|
|
96
|
+
all_connection_items: list["ConnectionItem"] = list()
|
|
97
97
|
all_connection_xml = parsed_response.findall(".//t:connection", namespaces=ns)
|
|
98
98
|
|
|
99
99
|
for connection_xml in all_connection_xml:
|
|
@@ -2,7 +2,8 @@ from datetime import datetime
|
|
|
2
2
|
|
|
3
3
|
from defusedxml import ElementTree
|
|
4
4
|
from defusedxml.ElementTree import fromstring, tostring
|
|
5
|
-
from typing import Callable,
|
|
5
|
+
from typing import Callable, Optional
|
|
6
|
+
from collections.abc import Iterator
|
|
6
7
|
|
|
7
8
|
from .exceptions import UnpopulatedPropertyError
|
|
8
9
|
from .user_item import UserItem
|
|
@@ -11,12 +12,14 @@ from .workbook_item import WorkbookItem
|
|
|
11
12
|
from ..datetime_helpers import parse_datetime
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
class CustomViewItem
|
|
15
|
+
class CustomViewItem:
|
|
15
16
|
def __init__(self, id: Optional[str] = None, name: Optional[str] = None) -> None:
|
|
16
17
|
self._content_url: Optional[str] = None # ?
|
|
17
18
|
self._created_at: Optional["datetime"] = None
|
|
18
19
|
self._id: Optional[str] = id
|
|
19
20
|
self._image: Optional[Callable[[], bytes]] = None
|
|
21
|
+
self._pdf: Optional[Callable[[], bytes]] = None
|
|
22
|
+
self._csv: Optional[Callable[[], Iterator[bytes]]] = None
|
|
20
23
|
self._name: Optional[str] = name
|
|
21
24
|
self._shared: Optional[bool] = False
|
|
22
25
|
self._updated_at: Optional["datetime"] = None
|
|
@@ -35,11 +38,17 @@ class CustomViewItem(object):
|
|
|
35
38
|
owner_info = ""
|
|
36
39
|
if self._owner:
|
|
37
40
|
owner_info = " owner='{}'".format(self._owner.name or self._owner.id or "unknown")
|
|
38
|
-
return "<CustomViewItem id={} name=`{}`{}{}{}>"
|
|
41
|
+
return f"<CustomViewItem id={self.id} name=`{self.name}`{view_info}{wb_info}{owner_info}>"
|
|
39
42
|
|
|
40
43
|
def _set_image(self, image):
|
|
41
44
|
self._image = image
|
|
42
45
|
|
|
46
|
+
def _set_pdf(self, pdf):
|
|
47
|
+
self._pdf = pdf
|
|
48
|
+
|
|
49
|
+
def _set_csv(self, csv):
|
|
50
|
+
self._csv = csv
|
|
51
|
+
|
|
43
52
|
@property
|
|
44
53
|
def content_url(self) -> Optional[str]:
|
|
45
54
|
return self._content_url
|
|
@@ -55,10 +64,24 @@ class CustomViewItem(object):
|
|
|
55
64
|
@property
|
|
56
65
|
def image(self) -> bytes:
|
|
57
66
|
if self._image is None:
|
|
58
|
-
error = "View item must be populated with its png image first."
|
|
67
|
+
error = "Custom View item must be populated with its png image first."
|
|
59
68
|
raise UnpopulatedPropertyError(error)
|
|
60
69
|
return self._image()
|
|
61
70
|
|
|
71
|
+
@property
|
|
72
|
+
def pdf(self) -> bytes:
|
|
73
|
+
if self._pdf is None:
|
|
74
|
+
error = "Custom View item must be populated with its pdf first."
|
|
75
|
+
raise UnpopulatedPropertyError(error)
|
|
76
|
+
return self._pdf()
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def csv(self) -> Iterator[bytes]:
|
|
80
|
+
if self._csv is None:
|
|
81
|
+
error = "Custom View item must be populated with its csv first."
|
|
82
|
+
raise UnpopulatedPropertyError(error)
|
|
83
|
+
return self._csv()
|
|
84
|
+
|
|
62
85
|
@property
|
|
63
86
|
def name(self) -> Optional[str]:
|
|
64
87
|
return self._name
|
|
@@ -104,7 +127,7 @@ class CustomViewItem(object):
|
|
|
104
127
|
return item[0]
|
|
105
128
|
|
|
106
129
|
@classmethod
|
|
107
|
-
def list_from_response(cls, resp, ns, workbook_id="") ->
|
|
130
|
+
def list_from_response(cls, resp, ns, workbook_id="") -> list["CustomViewItem"]:
|
|
108
131
|
return cls.from_xml_element(fromstring(resp), ns, workbook_id)
|
|
109
132
|
|
|
110
133
|
"""
|
|
@@ -121,7 +144,7 @@ class CustomViewItem(object):
|
|
|
121
144
|
"""
|
|
122
145
|
|
|
123
146
|
@classmethod
|
|
124
|
-
def from_xml_element(cls, parsed_response, ns, workbook_id="") ->
|
|
147
|
+
def from_xml_element(cls, parsed_response, ns, workbook_id="") -> list["CustomViewItem"]:
|
|
125
148
|
all_view_items = list()
|
|
126
149
|
all_view_xml = parsed_response.findall(".//t:customView", namespaces=ns)
|
|
127
150
|
for custom_view_xml in all_view_xml:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ from .property_decorators import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class DataAlertItem
|
|
13
|
+
class DataAlertItem:
|
|
14
14
|
class Frequency:
|
|
15
15
|
Once = "Once"
|
|
16
16
|
Frequently = "Frequently"
|
|
@@ -34,7 +34,7 @@ class DataAlertItem(object):
|
|
|
34
34
|
self._workbook_name: Optional[str] = None
|
|
35
35
|
self._project_id: Optional[str] = None
|
|
36
36
|
self._project_name: Optional[str] = None
|
|
37
|
-
self._recipients: Optional[
|
|
37
|
+
self._recipients: Optional[list[str]] = None
|
|
38
38
|
|
|
39
39
|
def __repr__(self) -> str:
|
|
40
40
|
return "<Data Alert {_id} subject={_subject} frequency={_frequency} \
|
|
@@ -78,7 +78,7 @@ class DataAlertItem(object):
|
|
|
78
78
|
return self._creatorId
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
|
-
def recipients(self) ->
|
|
81
|
+
def recipients(self) -> list[str]:
|
|
82
82
|
return self._recipients or list()
|
|
83
83
|
|
|
84
84
|
@property
|
|
@@ -174,7 +174,7 @@ class DataAlertItem(object):
|
|
|
174
174
|
self._recipients = recipients
|
|
175
175
|
|
|
176
176
|
@classmethod
|
|
177
|
-
def from_response(cls, resp, ns) ->
|
|
177
|
+
def from_response(cls, resp, ns) -> list["DataAlertItem"]:
|
|
178
178
|
all_alert_items = list()
|
|
179
179
|
parsed_response = fromstring(resp)
|
|
180
180
|
all_alert_xml = parsed_response.findall(".//t:dataAlert", namespaces=ns)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import xml.etree.ElementTree as ET
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
from tableauserverclient.models.property_decorators import property_is_enum, property_not_nullable
|
|
5
5
|
from .interval_item import IntervalItem
|
|
6
6
|
|
|
@@ -50,11 +50,11 @@ class DataFreshnessPolicyItem:
|
|
|
50
50
|
Week = "Week"
|
|
51
51
|
Month = "Month"
|
|
52
52
|
|
|
53
|
-
def __init__(self, frequency: str, time: str, timezone, interval_item: Optional[
|
|
53
|
+
def __init__(self, frequency: str, time: str, timezone, interval_item: Optional[list[str]] = None):
|
|
54
54
|
self.frequency = frequency
|
|
55
55
|
self.time = time
|
|
56
56
|
self.timezone = timezone
|
|
57
|
-
self.interval_item: Optional[
|
|
57
|
+
self.interval_item: Optional[list[str]] = interval_item
|
|
58
58
|
|
|
59
59
|
def __repr__(self):
|
|
60
60
|
return (
|
|
@@ -62,11 +62,11 @@ class DataFreshnessPolicyItem:
|
|
|
62
62
|
).format(**vars(self))
|
|
63
63
|
|
|
64
64
|
@property
|
|
65
|
-
def interval_item(self) -> Optional[
|
|
65
|
+
def interval_item(self) -> Optional[list[str]]:
|
|
66
66
|
return self._interval_item
|
|
67
67
|
|
|
68
68
|
@interval_item.setter
|
|
69
|
-
def interval_item(self, value:
|
|
69
|
+
def interval_item(self, value: list[str]):
|
|
70
70
|
self._interval_item = value
|
|
71
71
|
|
|
72
72
|
@property
|
|
@@ -186,7 +186,7 @@ def parse_week_intervals(interval_values):
|
|
|
186
186
|
|
|
187
187
|
|
|
188
188
|
def parse_month_intervals(interval_values):
|
|
189
|
-
error = "Invalid interval value for a monthly frequency: {}."
|
|
189
|
+
error = f"Invalid interval value for a monthly frequency: {interval_values}."
|
|
190
190
|
|
|
191
191
|
# Month interval can have value either only ['LastDay'] or list of dates e.g. ["1", 20", "30"]
|
|
192
192
|
# First check if the list only have LastDay value. When using LastDay, there shouldn't be
|
|
@@ -10,7 +10,7 @@ from .property_decorators import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class DatabaseItem
|
|
13
|
+
class DatabaseItem:
|
|
14
14
|
class ContentPermissions:
|
|
15
15
|
LockedToProject = "LockedToDatabase"
|
|
16
16
|
ManagedByOwner = "ManagedByOwner"
|
|
@@ -45,7 +45,7 @@ class DatabaseItem(object):
|
|
|
45
45
|
self._tables = None # Not implemented yet
|
|
46
46
|
|
|
47
47
|
def __str__(self):
|
|
48
|
-
return "<Database {
|
|
48
|
+
return f"<Database {self._id} '{self.name}'>"
|
|
49
49
|
|
|
50
50
|
def __repr__(self):
|
|
51
51
|
return self.__str__() + " { " + ", ".join(" % s: % s" % item for item in vars(self).items()) + "}"
|
|
@@ -250,7 +250,7 @@ class DatabaseItem(object):
|
|
|
250
250
|
self._tables = tables
|
|
251
251
|
|
|
252
252
|
def _set_default_permissions(self, permissions, content_type):
|
|
253
|
-
attr = "_default_{
|
|
253
|
+
attr = f"_default_{content_type}_permissions"
|
|
254
254
|
setattr(
|
|
255
255
|
self,
|
|
256
256
|
attr,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import datetime
|
|
3
3
|
import xml.etree.ElementTree as ET
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from defusedxml.ElementTree import fromstring
|
|
7
7
|
|
|
@@ -18,14 +18,14 @@ from tableauserverclient.models.revision_item import RevisionItem
|
|
|
18
18
|
from tableauserverclient.models.tag_item import TagItem
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class DatasourceItem
|
|
21
|
+
class DatasourceItem:
|
|
22
22
|
class AskDataEnablement:
|
|
23
23
|
Enabled = "Enabled"
|
|
24
24
|
Disabled = "Disabled"
|
|
25
25
|
SiteDefault = "SiteDefault"
|
|
26
26
|
|
|
27
27
|
def __repr__(self):
|
|
28
|
-
return "<Datasource {
|
|
28
|
+
return "<Datasource {} '{}' ({} parent={} >".format(
|
|
29
29
|
self._id,
|
|
30
30
|
self.name,
|
|
31
31
|
self.description or "No Description",
|
|
@@ -44,7 +44,7 @@ class DatasourceItem(object):
|
|
|
44
44
|
self._encrypt_extracts = None
|
|
45
45
|
self._has_extracts = None
|
|
46
46
|
self._id: Optional[str] = None
|
|
47
|
-
self._initial_tags:
|
|
47
|
+
self._initial_tags: set = set()
|
|
48
48
|
self._project_name: Optional[str] = None
|
|
49
49
|
self._revisions = None
|
|
50
50
|
self._size: Optional[int] = None
|
|
@@ -55,7 +55,7 @@ class DatasourceItem(object):
|
|
|
55
55
|
self.name = name
|
|
56
56
|
self.owner_id: Optional[str] = None
|
|
57
57
|
self.project_id = project_id
|
|
58
|
-
self.tags:
|
|
58
|
+
self.tags: set[str] = set()
|
|
59
59
|
|
|
60
60
|
self._permissions = None
|
|
61
61
|
self._data_quality_warnings = None
|
|
@@ -72,14 +72,14 @@ class DatasourceItem(object):
|
|
|
72
72
|
self._ask_data_enablement = value
|
|
73
73
|
|
|
74
74
|
@property
|
|
75
|
-
def connections(self) -> Optional[
|
|
75
|
+
def connections(self) -> Optional[list[ConnectionItem]]:
|
|
76
76
|
if self._connections is None:
|
|
77
77
|
error = "Datasource item must be populated with connections first."
|
|
78
78
|
raise UnpopulatedPropertyError(error)
|
|
79
79
|
return self._connections()
|
|
80
80
|
|
|
81
81
|
@property
|
|
82
|
-
def permissions(self) -> Optional[
|
|
82
|
+
def permissions(self) -> Optional[list[PermissionsRule]]:
|
|
83
83
|
if self._permissions is None:
|
|
84
84
|
error = "Project item must be populated with permissions first."
|
|
85
85
|
raise UnpopulatedPropertyError(error)
|
|
@@ -177,7 +177,7 @@ class DatasourceItem(object):
|
|
|
177
177
|
return self._webpage_url
|
|
178
178
|
|
|
179
179
|
@property
|
|
180
|
-
def revisions(self) ->
|
|
180
|
+
def revisions(self) -> list[RevisionItem]:
|
|
181
181
|
if self._revisions is None:
|
|
182
182
|
error = "Datasource item must be populated with revisions first."
|
|
183
183
|
raise UnpopulatedPropertyError(error)
|
|
@@ -309,7 +309,7 @@ class DatasourceItem(object):
|
|
|
309
309
|
self._size = int(size)
|
|
310
310
|
|
|
311
311
|
@classmethod
|
|
312
|
-
def from_response(cls, resp: str, ns:
|
|
312
|
+
def from_response(cls, resp: str, ns: dict) -> list["DatasourceItem"]:
|
|
313
313
|
all_datasource_items = list()
|
|
314
314
|
parsed_response = fromstring(resp)
|
|
315
315
|
all_datasource_xml = parsed_response.findall(".//t:datasource", namespaces=ns)
|
|
@@ -326,7 +326,7 @@ class DatasourceItem(object):
|
|
|
326
326
|
return datasource_item
|
|
327
327
|
|
|
328
328
|
@staticmethod
|
|
329
|
-
def _parse_element(datasource_xml: ET.Element, ns:
|
|
329
|
+
def _parse_element(datasource_xml: ET.Element, ns: dict) -> tuple:
|
|
330
330
|
id_ = datasource_xml.get("id", None)
|
|
331
331
|
name = datasource_xml.get("name", None)
|
|
332
332
|
datasource_type = datasource_xml.get("type", None)
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
+
from typing import Union
|
|
3
4
|
from defusedxml.ElementTree import fromstring
|
|
4
|
-
from tableauserverclient.models.tableau_types import TableauItem
|
|
5
5
|
|
|
6
|
+
from tableauserverclient.models.tableau_types import TableauItem
|
|
6
7
|
from tableauserverclient.models.datasource_item import DatasourceItem
|
|
7
8
|
from tableauserverclient.models.flow_item import FlowItem
|
|
8
9
|
from tableauserverclient.models.project_item import ProjectItem
|
|
9
10
|
from tableauserverclient.models.metric_item import MetricItem
|
|
10
11
|
from tableauserverclient.models.view_item import ViewItem
|
|
11
12
|
from tableauserverclient.models.workbook_item import WorkbookItem
|
|
12
|
-
from typing import Dict, List
|
|
13
13
|
|
|
14
14
|
from tableauserverclient.helpers.logging import logger
|
|
15
|
-
from typing import Dict, List, Union
|
|
16
15
|
|
|
17
|
-
FavoriteType =
|
|
16
|
+
FavoriteType = dict[
|
|
18
17
|
str,
|
|
19
|
-
|
|
18
|
+
list[TableauItem],
|
|
20
19
|
]
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class FavoriteItem:
|
|
24
23
|
@classmethod
|
|
25
|
-
def from_response(cls, xml: str, namespace:
|
|
24
|
+
def from_response(cls, xml: Union[str, bytes], namespace: dict) -> FavoriteType:
|
|
26
25
|
favorites: FavoriteType = {
|
|
27
26
|
"datasources": [],
|
|
28
27
|
"flows": [],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import datetime
|
|
3
3
|
import xml.etree.ElementTree as ET
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Optional
|
|
5
5
|
|
|
6
6
|
from defusedxml.ElementTree import fromstring
|
|
7
7
|
|
|
@@ -14,9 +14,9 @@ from tableauserverclient.models.property_decorators import property_not_nullable
|
|
|
14
14
|
from tableauserverclient.models.tag_item import TagItem
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class FlowItem
|
|
17
|
+
class FlowItem:
|
|
18
18
|
def __repr__(self):
|
|
19
|
-
return "<Flow {
|
|
19
|
+
return "<Flow {} '{}' ({}) Project={} createdAt={}".format(
|
|
20
20
|
self._id, self.name, self.description, self.project_id, self.created_at
|
|
21
21
|
)
|
|
22
22
|
|
|
@@ -24,13 +24,13 @@ class FlowItem(object):
|
|
|
24
24
|
self._webpage_url: Optional[str] = None
|
|
25
25
|
self._created_at: Optional[datetime.datetime] = None
|
|
26
26
|
self._id: Optional[str] = None
|
|
27
|
-
self._initial_tags:
|
|
27
|
+
self._initial_tags: set[str] = set()
|
|
28
28
|
self._project_name: Optional[str] = None
|
|
29
29
|
self._updated_at: Optional[datetime.datetime] = None
|
|
30
30
|
self.name: Optional[str] = name
|
|
31
31
|
self.owner_id: Optional[str] = None
|
|
32
32
|
self.project_id: str = project_id
|
|
33
|
-
self.tags:
|
|
33
|
+
self.tags: set[str] = set()
|
|
34
34
|
self.description: Optional[str] = None
|
|
35
35
|
|
|
36
36
|
self._connections: Optional[ConnectionItem] = None
|
|
@@ -170,7 +170,7 @@ class FlowItem(object):
|
|
|
170
170
|
self.owner_id = owner_id
|
|
171
171
|
|
|
172
172
|
@classmethod
|
|
173
|
-
def from_response(cls, resp, ns) ->
|
|
173
|
+
def from_response(cls, resp, ns) -> list["FlowItem"]:
|
|
174
174
|
all_flow_items = list()
|
|
175
175
|
parsed_response = fromstring(resp)
|
|
176
176
|
all_flow_xml = parsed_response.findall(".//t:flow", namespaces=ns)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from defusedxml.ElementTree import fromstring
|
|
6
6
|
|
|
7
7
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class FlowRunItem
|
|
10
|
+
class FlowRunItem:
|
|
11
11
|
def __init__(self) -> None:
|
|
12
12
|
self._id: str = ""
|
|
13
13
|
self._flow_id: Optional[str] = None
|
|
@@ -71,7 +71,7 @@ class FlowRunItem(object):
|
|
|
71
71
|
self._background_job_id = background_job_id
|
|
72
72
|
|
|
73
73
|
@classmethod
|
|
74
|
-
def from_response(cls:
|
|
74
|
+
def from_response(cls: type["FlowRunItem"], resp: bytes, ns: Optional[dict]) -> list["FlowRunItem"]:
|
|
75
75
|
all_flowrun_items = list()
|
|
76
76
|
parsed_response = fromstring(resp)
|
|
77
77
|
all_flowrun_xml = itertools.chain(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Callable,
|
|
1
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from defusedxml.ElementTree import fromstring
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
|
|
11
11
|
from tableauserverclient.server import Pager
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class GroupItem
|
|
14
|
+
class GroupItem:
|
|
15
15
|
tag_name: str = "group"
|
|
16
16
|
|
|
17
17
|
class LicenseMode:
|
|
@@ -27,7 +27,7 @@ class GroupItem(object):
|
|
|
27
27
|
self.domain_name: Optional[str] = domain_name
|
|
28
28
|
|
|
29
29
|
def __repr__(self):
|
|
30
|
-
return "{
|
|
30
|
+
return f"{self.__class__.__name__}({self.__dict__!r})"
|
|
31
31
|
|
|
32
32
|
@property
|
|
33
33
|
def domain_name(self) -> Optional[str]:
|
|
@@ -79,7 +79,7 @@ class GroupItem(object):
|
|
|
79
79
|
self._users = users
|
|
80
80
|
|
|
81
81
|
@classmethod
|
|
82
|
-
def from_response(cls, resp, ns) ->
|
|
82
|
+
def from_response(cls, resp, ns) -> list["GroupItem"]:
|
|
83
83
|
all_group_items = list()
|
|
84
84
|
parsed_response = fromstring(resp)
|
|
85
85
|
all_group_xml = parsed_response.findall(".//t:group", namespaces=ns)
|